diff --git a/go.mod b/go.mod index 02740b6..a66a993 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module tinygo.org/x/drivers go 1.14 -require github.com/eclipse/paho.mqtt.golang v1.2.0 +require ( + github.com/eclipse/paho.mqtt.golang v1.2.0 + github.com/frankban/quicktest v1.10.2 +) diff --git a/go.sum b/go.sum index 9dfbb0b..2c57487 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,13 @@ github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t2y2qayIX0= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= +github.com/frankban/quicktest v1.10.2 h1:19ARM85nVi4xH7xPXuc5eM/udya5ieh7b/Sv+d844Tk= +github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/i2c.go b/i2c.go new file mode 100644 index 0000000..27bc27d --- /dev/null +++ b/i2c.go @@ -0,0 +1,9 @@ +package drivers + +// I2C represents an I2C bus. It is notably implemented by the +// machine.I2C type. +type I2C interface { + ReadRegister(addr uint8, r uint8, buf []byte) error + WriteRegister(addr uint8, r uint8, buf []byte) error + Tx(addr uint16, w, r []byte) error +} diff --git a/tester/device.go b/tester/device.go new file mode 100644 index 0000000..43b669b --- /dev/null +++ b/tester/device.go @@ -0,0 +1,58 @@ +package tester + +import ( + qt "github.com/frankban/quicktest" +) + +const maxRegisters = 200 + +// I2CDevice represents a mock I2C device on a mock I2C bus. +type I2CDevice struct { + C *qt.C + 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. +func NewI2CDevice(c *qt.C, addr uint8) *I2CDevice { + return &I2CDevice{ + C: c, + Addr: 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)) + } +} diff --git a/tester/i2c.go b/tester/i2c.go new file mode 100644 index 0000000..79887c3 --- /dev/null +++ b/tester/i2c.go @@ -0,0 +1,52 @@ +package tester + +import ( + qt "github.com/frankban/quicktest" +) + +// I2CBus implements the I2C interface in memory for testing. +type I2CBus struct { + C *qt.C + Devices []*I2CDevice +} + +// NewI2CBus returns an I2CBus mock I2C instance that uses c to flag errors +// if they happen. After creating a I2C instance, add devices +// to it with addDevice before using NewI2CBus interface. +func NewI2CBus(c *qt.C) *I2CBus { + return &I2CBus{ + C: c, + } +} + +// AddDevice adds a new mock device to the mock I2C bus. +func (bus *I2CBus) AddDevice(d *I2CDevice) { + bus.Devices = append(bus.Devices, d) +} + +// ReadRegister implements I2C.ReadRegister. +func (bus *I2CBus) ReadRegister(addr uint8, r uint8, buf []byte) error { + return bus.FindDevice(addr).ReadRegister(r, buf) +} + +// WriteRegister implements I2C.WriteRegister. +func (bus *I2CBus) WriteRegister(addr uint8, r uint8, buf []byte) error { + return bus.FindDevice(addr).WriteRegister(r, buf) +} + +// Tx implements I2C.Tx. +func (bus *I2CBus) Tx(addr uint16, input, output []byte) error { + // TODO: implement this + return nil +} + +// FindDevice returns the device with the given address. +func (bus *I2CBus) FindDevice(addr uint8) *I2CDevice { + for _, dev := range bus.Devices { + if dev.Addr == addr { + return dev + } + } + bus.C.Fatalf("invalid device addr %#x passed to i2c bus", addr) + panic("unreachable") +} diff --git a/tester/tester.go b/tester/tester.go new file mode 100644 index 0000000..5caf27e --- /dev/null +++ b/tester/tester.go @@ -0,0 +1,5 @@ +// Package tester contains mock structs to make it easier to test I2C devices. +// +// TODO: info on how to use this. +// +package tester // import "tinygo.org/x/drivers/tester"