hybridgroup.gobot/drivers/i2c/ccs811_driver_test.go

268 lines
7.4 KiB
Go

package i2c
import (
"errors"
"strings"
"testing"
"gobot.io/x/gobot/v2"
"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 = (*CCS811Driver)(nil)
func initTestCCS811WithStubbedAdaptor() (*CCS811Driver, *i2cTestAdaptor) {
a := newI2cTestAdaptor()
return NewCCS811Driver(a), a
}
func TestNewCCS811Driver(t *testing.T) {
var di interface{} = NewCCS811Driver(newI2cTestAdaptor())
d, ok := di.(*CCS811Driver)
if !ok {
t.Errorf("NewCCS811Driver() should have returned a *CCS811Driver")
}
gobottest.Refute(t, d.Driver, nil)
gobottest.Assert(t, strings.HasPrefix(d.Name(), "CCS811"), true)
gobottest.Assert(t, d.defaultAddress, 0x5A)
gobottest.Refute(t, d.measMode, nil)
gobottest.Assert(t, d.ntcResistanceValue, uint32(100000))
}
func TestCCS811Options(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 := NewCCS811Driver(newI2cTestAdaptor(), WithBus(2), WithAddress(0xFF), WithCCS811NTCResistance(0xFF))
gobottest.Assert(t, d.GetBusOrDefault(1), 2)
gobottest.Assert(t, d.GetAddressOrDefault(0x5a), 0xFF)
gobottest.Assert(t, d.ntcResistanceValue, uint32(0xFF))
}
func TestCCS811WithCCS811MeasMode(t *testing.T) {
d := NewCCS811Driver(newI2cTestAdaptor(), WithCCS811MeasMode(CCS811DriveMode10Sec))
gobottest.Assert(t, d.measMode.driveMode, CCS811DriveMode(CCS811DriveMode10Sec))
}
func TestCCS811GetGasData(t *testing.T) {
var tests = map[string]struct {
readReturn func([]byte) (int, error)
eco2 uint16
tvoc uint16
err error
}{
"ideal values taken from the bus": {
readReturn: func(b []byte) (int, error) {
copy(b, []byte{1, 156, 0, 86})
return 4, nil
},
eco2: 412,
tvoc: 86,
err: nil,
},
"max values possible taken from the bus": {
readReturn: func(b []byte) (int, error) {
copy(b, []byte{255, 255, 255, 255})
return 4, nil
},
eco2: 65535,
tvoc: 65535,
err: nil,
},
"error when the i2c operation fails": {
readReturn: func(b []byte) (int, error) {
copy(b, []byte{255, 255, 255, 255})
return 4, errors.New("Error")
},
eco2: 0,
tvoc: 0,
err: errors.New("Error"),
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
// arrange
d, a := initTestCCS811WithStubbedAdaptor()
// Create stub function as it is needed by read submethod in driver code
a.i2cWriteImpl = func([]byte) (int, error) { return 0, nil }
_ = d.Start()
a.i2cReadImpl = tc.readReturn
// act
eco2, tvoc, err := d.GetGasData()
// assert
gobottest.Assert(t, eco2, tc.eco2)
gobottest.Assert(t, tvoc, tc.tvoc)
gobottest.Assert(t, err, tc.err)
})
}
}
func TestCCS811GetTemperature(t *testing.T) {
var tests = map[string]struct {
readReturn func([]byte) (int, error)
temp float32
err error
}{
"ideal values taken from the bus": {
readReturn: func(b []byte) (int, error) {
copy(b, []byte{10, 197, 0, 248})
return 4, nil
},
temp: 27.811005,
err: nil,
},
"without bus values overflowing": {
readReturn: func(b []byte) (int, error) {
copy(b, []byte{129, 197, 10, 248})
return 4, nil
},
temp: 29.48822,
err: nil,
},
"negative temperature": {
readReturn: func(b []byte) (int, error) {
copy(b, []byte{255, 255, 255, 255})
return 4, nil
},
temp: -25.334152,
err: nil,
},
"error if the i2c bus errors": {
readReturn: func(b []byte) (int, error) {
copy(b, []byte{129, 197, 0, 248})
return 4, errors.New("Error")
},
temp: 0,
err: errors.New("Error"),
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
// arrange
d, a := initTestCCS811WithStubbedAdaptor()
// Create stub function as it is needed by read submethod in driver code
a.i2cWriteImpl = func([]byte) (int, error) { return 0, nil }
_ = d.Start()
a.i2cReadImpl = tc.readReturn
// act
temp, err := d.GetTemperature()
// assert
gobottest.Assert(t, temp, tc.temp)
gobottest.Assert(t, err, tc.err)
})
}
}
func TestCCS811HasData(t *testing.T) {
var tests = map[string]struct {
readReturn func([]byte) (int, error)
result bool
err error
}{
"true for HasError=0 and DataRdy=1": {
readReturn: func(b []byte) (int, error) {
copy(b, []byte{0x08})
return 1, nil
},
result: true,
err: nil,
},
"false for HasError=1 and DataRdy=1": {
readReturn: func(b []byte) (int, error) {
copy(b, []byte{0x09})
return 1, nil
},
result: false,
err: nil,
},
"false for HasError=1 and DataRdy=0": {
readReturn: func(b []byte) (int, error) {
copy(b, []byte{0x01})
return 1, nil
},
result: false,
err: nil,
},
"false for HasError=0 and DataRdy=0": {
readReturn: func(b []byte) (int, error) {
copy(b, []byte{0x00})
return 1, nil
},
result: false,
err: nil,
},
"error when the i2c read operation fails": {
readReturn: func(b []byte) (int, error) {
copy(b, []byte{0x00})
return 1, errors.New("Error")
},
result: false,
err: errors.New("Error"),
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
// arrange
d, a := initTestCCS811WithStubbedAdaptor()
// Create stub function as it is needed by read submethod in driver code
a.i2cWriteImpl = func([]byte) (int, error) { return 0, nil }
_ = d.Start()
a.i2cReadImpl = tc.readReturn
// act
result, err := d.HasData()
// assert
gobottest.Assert(t, result, tc.result)
gobottest.Assert(t, err, tc.err)
})
}
}
func TestCCS811_initialize(t *testing.T) {
// sequence for initialization the device on Start()
// * write hardware ID register (0x20)
// * read the ID
// * prepare software reset register content: a sequence of four bytes must
// be written to this register in a single I²C sequence: 0x11, 0xE5, 0x72, 0x8A
// * write software reset register content (0xFF)
// * write application start register (0xF4)
// * prepare measurement mode register content
// * INT_THRESH = 0 (normal mode)
// * INT_DATARDY = 0 (disable interrupt mode)
// * DRIVE_MODE = 0x01 (constant power, value every 1 sec)
// * write measure mode register content (0x01)
//
// arrange
d, a := initTestCCS811WithStubbedAdaptor()
a.written = []byte{} // reset writes of former test
const (
wantChipIDReg = uint8(0x20)
wantChipIDRegVal = uint8(0x20)
wantResetReg = uint8(0xFF)
wantAppStartReg = uint8(0xF4)
wantMeasReg = uint8(0x01)
wantMeasRegVal = uint8(0x10)
)
wantResetRegSequence := []byte{0x11, 0xE5, 0x72, 0x8A}
// arrange reads
numCallsRead := 0
a.i2cReadImpl = func(b []byte) (int, error) {
numCallsRead++
// chip ID
b[0] = 0x81
return len(b), nil
}
// arrange, act - initialize() must be called on Start()
err := d.Start()
// assert
gobottest.Assert(t, err, nil)
gobottest.Assert(t, numCallsRead, 1)
gobottest.Assert(t, len(a.written), 9)
gobottest.Assert(t, a.written[0], wantChipIDReg)
gobottest.Assert(t, a.written[1], wantResetReg)
gobottest.Assert(t, a.written[2:6], wantResetRegSequence)
gobottest.Assert(t, a.written[6], wantAppStartReg)
gobottest.Assert(t, a.written[7], wantMeasReg)
gobottest.Assert(t, a.written[8], wantMeasRegVal)
}