diff --git a/mcp23017/device_test.go b/mcp23017/device_test.go index 4a4cbe5..342252b 100644 --- a/mcp23017/device_test.go +++ b/mcp23017/device_test.go @@ -211,7 +211,7 @@ func TestInitWithError(t *testing.T) { c.Assert(dev, qt.IsNil) } -func newDevice(bus *tester.I2CBus, addr uint8) *tester.I2CDevice { +func newDevice(bus *tester.I2CBus, addr uint8) *tester.I2CDevice8 { fdev := bus.NewDevice(addr) // IODIRA and IODIRB are all ones by default. fdev.Registers[rIODIR] = 0xff diff --git a/tester/device.go b/tester/device.go index 2a5a0b0..9a9648d 100644 --- a/tester/device.go +++ b/tester/device.go @@ -1,61 +1,15 @@ package tester // MaxRegisters is the maximum number of registers supported for a Device. -const MaxRegisters = 200 +const MaxRegisters = 255 -// I2CDevice represents a mock I2C device on a mock I2C bus. -type I2CDevice struct { - c Failer - // addr is the i2c device address. - addr uint8 - // Registers holds the device registers. It can be inspected - // or changed as desired for testing. - Registers [MaxRegisters]uint8 - // If Err is non-nil, it will be returned as the error from the - // I2C methods. - Err error -} +type I2CDevice interface { + // ReadRegister implements I2C.ReadRegister. + ReadRegister(r uint8, buf []byte) error -// NewI2CDevice returns a new mock I2C device. -func NewI2CDevice(c Failer, addr uint8) *I2CDevice { - return &I2CDevice{ - c: c, - addr: addr, - } -} + // WriteRegister implements I2C.WriteRegister. + WriteRegister(r uint8, buf []byte) error -// Addr returns the Device address. -func (d *I2CDevice) Addr() uint8 { - return d.addr -} - -// ReadRegister implements I2C.ReadRegister. -func (d *I2CDevice) ReadRegister(r uint8, buf []byte) error { - if d.Err != nil { - return d.Err - } - d.assertRegisterRange(r, buf) - copy(buf, d.Registers[r:]) - return nil -} - -// WriteRegister implements I2C.WriteRegister. -func (d *I2CDevice) WriteRegister(r uint8, buf []byte) error { - if d.Err != nil { - return d.Err - } - d.assertRegisterRange(r, buf) - copy(d.Registers[r:], buf) - return nil -} - -// assertRegisterRange asserts that reading or writing the given -// register and subsequent registers is in range of the available registers. -func (d *I2CDevice) assertRegisterRange(r uint8, buf []byte) { - if int(r) >= len(d.Registers) { - d.c.Fatalf("register read/write [%#x, %#x] start out of range", r, int(r)+len(buf)) - } - if int(r)+len(buf) > len(d.Registers) { - d.c.Fatalf("register read/write [%#x, %#x] end out of range", r, int(r)+len(buf)) - } + // Addr returns the Device address. + Addr() uint8 } diff --git a/tester/device16.go b/tester/device16.go new file mode 100644 index 0000000..deb4ce2 --- /dev/null +++ b/tester/device16.go @@ -0,0 +1,74 @@ +package tester + +// I2CDevice represents a mock I2C device on a mock I2C bus with 16-bit registers. +type I2CDevice16 struct { + c Failer + // addr is the i2c device address. + addr uint8 + // Registers holds the device registers. It can be inspected + // or changed as desired for testing. + Registers map[uint8]uint16 + // If Err is non-nil, it will be returned as the error from the + // I2C methods. + Err error +} + +// NewI2CDevice returns a new mock I2C device. +// +// To use this mock, populate the Registers map with known / expected +// registers. Attempts by the code under test to write to a register +// that has not been populated into the map will be treated as an +// error. +func NewI2CDevice16(c Failer, addr uint8) *I2CDevice16 { + return &I2CDevice16{ + c: c, + addr: addr, + Registers: map[uint8]uint16{}, + } +} + +// Addr returns the Device address. +func (d *I2CDevice16) Addr() uint8 { + return d.addr +} + +// ReadRegister implements I2C.ReadRegister. +func (d *I2CDevice16) ReadRegister(r uint8, buf []byte) error { + if d.Err != nil { + return d.Err + } + + if len(buf) > 2 { + d.c.Fatalf("register read [%#x, %#x] oversized buffer", r, len(buf)) + } + + val, ok := d.Registers[r] + if !ok { + d.c.Fatalf("register read [%#x] unknown register", r) + } + + buf[0] = byte(val >> 8) + buf[1] = byte(val & 0xff) + + return nil +} + +// WriteRegister implements I2C.WriteRegister. +func (d *I2CDevice16) WriteRegister(r uint8, buf []byte) error { + if d.Err != nil { + return d.Err + } + + if len(buf) != 2 { + d.c.Fatalf("register write [%#x, %#x] mis-sized write", r, len(buf)) + } + + _, ok := d.Registers[r] + if !ok { + d.c.Fatalf("register write [%#x] unknown register", r) + } + + d.Registers[r] = uint16(buf[0])<<8 | uint16(buf[1]) + + return nil +} diff --git a/tester/device16_test.go b/tester/device16_test.go new file mode 100644 index 0000000..87c8ae3 --- /dev/null +++ b/tester/device16_test.go @@ -0,0 +1,42 @@ +package tester + +import ( + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestCreate16(t *testing.T) { + c := qt.New(t) + bus := NewI2CBus(c) + d := NewI2CDevice16(c, 8) + bus.AddDevice(d) +} + +func TestRead16(t *testing.T) { + c := qt.New(t) + bus := NewI2CBus(c) + d := NewI2CDevice16(c, 8) + bus.AddDevice(d) + + // Setup a random register + d.Registers[3] = 0x1234 + + buf := []byte{0, 0} + err := bus.ReadRegister(8, 3, buf) + c.Assert(err, qt.IsNil) + c.Assert(buf[0], qt.Equals, byte(0x12)) + c.Assert(buf[1], qt.Equals, byte(0x34)) +} + +func TestWrite16(t *testing.T) { + c := qt.New(t) + bus := NewI2CBus(c) + d := NewI2CDevice16(c, 8) + bus.AddDevice(d) + + d.Registers[9] = 0x0 + err := bus.WriteRegister(8, 9, []byte{0xbe, 0xad}) + c.Assert(err, qt.IsNil) + c.Assert(d.Registers[9], qt.Equals, uint16(0xbead)) +} diff --git a/tester/device8.go b/tester/device8.go new file mode 100644 index 0000000..2c58933 --- /dev/null +++ b/tester/device8.go @@ -0,0 +1,65 @@ +package tester + +// I2CDevice represents a mock I2C device on a mock I2C bus with 8-bit registers. +type I2CDevice8 struct { + c Failer + // addr is the i2c device address. + addr uint8 + // Registers holds the device registers. It can be inspected + // or changed as desired for testing. + Registers [MaxRegisters]uint8 + // If Err is non-nil, it will be returned as the error from the + // I2C methods. + Err error +} + +// NewI2CDevice returns a new mock I2C device. +// +// For compatibility, this creates an instance of NewI2CDevice8 +func NewI2CDevice(c Failer, addr uint8) *I2CDevice8 { + return NewI2CDevice8(c, addr) +} + +// NewI2CDevice8 returns a new mock I2C device. +func NewI2CDevice8(c Failer, addr uint8) *I2CDevice8 { + return &I2CDevice8{ + c: c, + addr: addr, + } +} + +// Addr returns the Device address. +func (d *I2CDevice8) Addr() uint8 { + return d.addr +} + +// ReadRegister implements I2C.ReadRegister. +func (d *I2CDevice8) ReadRegister(r uint8, buf []byte) error { + if d.Err != nil { + return d.Err + } + d.assertRegisterRange(r, buf) + copy(buf, d.Registers[r:]) + return nil +} + +// WriteRegister implements I2C.WriteRegister. +func (d *I2CDevice8) WriteRegister(r uint8, buf []byte) error { + if d.Err != nil { + return d.Err + } + d.assertRegisterRange(r, buf) + copy(d.Registers[r:], buf) + return nil +} + +// assertRegisterRange asserts that reading or writing the given +// register and subsequent registers is in range of the available registers. +func (d *I2CDevice8) assertRegisterRange(r uint8, buf []byte) { + if int(r) >= len(d.Registers) { + d.c.Fatalf("register read/write [%#x, %#x] start out of range", r, int(r)+len(buf)) + } + if int(r)+len(buf) > len(d.Registers) { + d.c.Fatalf("register read/write [%#x, %#x] end out of range", r, int(r)+len(buf)) + } +} diff --git a/tester/device8_test.go b/tester/device8_test.go new file mode 100644 index 0000000..eb59a64 --- /dev/null +++ b/tester/device8_test.go @@ -0,0 +1,43 @@ +package tester + +import ( + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestCreate8(t *testing.T) { + c := qt.New(t) + bus := NewI2CBus(c) + d := NewI2CDevice8(c, 8) + bus.AddDevice(d) +} + +func TestRead8(t *testing.T) { + c := qt.New(t) + bus := NewI2CBus(c) + d := NewI2CDevice8(c, 8) + bus.AddDevice(d) + + // Setup a random register + d.Registers[3] = 0x12 + d.Registers[4] = 0x34 + + buf := []byte{0, 0} + err := bus.ReadRegister(8, 3, buf) + c.Assert(err, qt.IsNil) + c.Assert(buf[0], qt.Equals, byte(0x12)) + c.Assert(buf[1], qt.Equals, byte(0x34)) +} + +func TestWrite8(t *testing.T) { + c := qt.New(t) + bus := NewI2CBus(c) + d := NewI2CDevice8(c, 8) + bus.AddDevice(d) + + err := bus.WriteRegister(8, 9, []byte{0xbe, 0xad}) + c.Assert(err, qt.IsNil) + c.Assert(d.Registers[9], qt.Equals, uint8(0xbe)) + c.Assert(d.Registers[10], qt.Equals, uint8(0xad)) +} diff --git a/tester/i2c.go b/tester/i2c.go index c80b9f2..1d86d14 100644 --- a/tester/i2c.go +++ b/tester/i2c.go @@ -5,7 +5,7 @@ import "fmt" // I2CBus implements the I2C interface in memory for testing. type I2CBus struct { c Failer - devices []*I2CDevice + devices []I2CDevice } // NewI2CBus returns an I2CBus mock I2C instance that uses c to flag errors @@ -19,9 +19,9 @@ func NewI2CBus(c Failer) *I2CBus { // AddDevice adds a new mock device to the mock I2C bus. // It panics if a device with the same address is added more than once. -func (bus *I2CBus) AddDevice(d *I2CDevice) { +func (bus *I2CBus) AddDevice(d I2CDevice) { for _, dev := range bus.devices { - if dev.Addr() == d.addr { + if dev.Addr() == d.Addr() { panic(fmt.Errorf("device already added at address %#x", d)) } } @@ -30,8 +30,8 @@ func (bus *I2CBus) AddDevice(d *I2CDevice) { // NewDevice creates a new device with the given address // and adds it to the mock I2C bus. -func (bus *I2CBus) NewDevice(addr uint8) *I2CDevice { - dev := NewI2CDevice(bus.c, addr) +func (bus *I2CBus) NewDevice(addr uint8) *I2CDevice8 { + dev := NewI2CDevice8(bus.c, addr) bus.AddDevice(dev) return dev } @@ -53,7 +53,7 @@ func (bus *I2CBus) Tx(addr uint16, w, r []byte) error { } // FindDevice returns the device with the given address. -func (bus *I2CBus) FindDevice(addr uint8) *I2CDevice { +func (bus *I2CBus) FindDevice(addr uint8) I2CDevice { for _, dev := range bus.devices { if dev.Addr() == addr { return dev