From 45abf11acc11b7b0b1501f4719a130e42e5ea2ae Mon Sep 17 00:00:00 2001 From: Thomas Kohler Date: Wed, 26 Oct 2022 18:21:34 +0200 Subject: [PATCH] BUGFIX: tests with sysfs mocks --- Makefile | 6 +- drivers/i2c/i2c_connection_test.go | 56 +- drivers/i2c/i2c_driver.go | 171 +++- examples/tinkerboard_pcf8583_clock.go | 2 +- examples/tinkerboard_pcf8583_counter.go | 2 +- .../beaglebone/beaglebone_adaptor_test.go | 5 + platforms/chip/chip_adaptor_test.go | 18 + .../dragonboard/dragonboard_adaptor_test.go | 4 + .../intel-iot/edison/edison_adaptor_test.go | 42 + .../intel-iot/joule/joule_adaptor_test.go | 20 + platforms/jetson/jetson_adaptor_test.go | 11 +- platforms/jetson/pwm_pin_test.go | 1 + platforms/raspi/raspi_adaptor_test.go | 16 +- platforms/tinkerboard/adaptor_test.go | 16 + platforms/upboard/up2/adaptor_test.go | 16 + sysfs/README.md | 39 + sysfs/digital_pin_bench_test.go | 6 +- sysfs/digital_pin_test.go | 3 + sysfs/i2c_device.go | 73 +- sysfs/i2c_device_test.go | 771 +++++++++++------- sysfs/pwm_pin_test.go | 7 + sysfs/syscall.go | 64 +- 22 files changed, 957 insertions(+), 392 deletions(-) create mode 100644 sysfs/README.md diff --git a/Makefile b/Makefile index 7dba1b05..35248ac2 100644 --- a/Makefile +++ b/Makefile @@ -13,16 +13,18 @@ EXAMPLES := $(EXAMPLES_NO_GOCV) including_except := $(shell go list ./... | grep -v platforms/opencv) # Run tests on nearly all directories without test cache +# TODO: set back to parallel running (remove "-p 1"), when issue #878 is fixed test: - go test -count=1 -v $(including_except) + go test -p 1 -count=1 -v $(including_except) # Run tests with race detection test_race: go test -race $(including_except) # Test, generate and show coverage in browser +# TODO: set back to parallel running (remove "-p 1"), when issue #878 is fixed test_cover: - go test -v $(including_except) -coverprofile=coverage.txt ; \ + go test -p 1 -v $(including_except) -coverprofile=coverage.txt ; \ go tool cover -html=coverage.txt ; \ robeaux: diff --git a/drivers/i2c/i2c_connection_test.go b/drivers/i2c/i2c_connection_test.go index 11e3f3bb..b54814a7 100644 --- a/drivers/i2c/i2c_connection_test.go +++ b/drivers/i2c/i2c_connection_test.go @@ -13,6 +13,8 @@ import ( "gobot.io/x/gobot/sysfs" ) +const dev = "/dev/i2c-1" + func syscallImpl(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { if (trap == syscall.SYS_IOCTL) && (a2 == sysfs.I2C_FUNCS) { var funcPtr *uint64 = (*uint64)(unsafe.Pointer(a3)) @@ -31,38 +33,37 @@ func syscallImplFail(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errn } func initI2CDevice() I2cDevice { - fs := sysfs.NewMockFilesystem([]string{ - "/dev/i2c-1", - }) + fs := sysfs.NewMockFilesystem([]string{dev}) sysfs.SetFilesystem(fs) + sysfs.SetSyscall(&sysfs.MockSyscall{Impl: syscallImpl}) - sysfs.SetSyscall(&sysfs.MockSyscall{ - Impl: syscallImpl, - }) - i, _ := sysfs.NewI2cDevice("/dev/i2c-1") + i, _ := sysfs.NewI2cDevice(dev) return i } -func initI2CDeviceAddressError() I2cDevice { - fs := sysfs.NewMockFilesystem([]string{ - "/dev/i2c-1", - }) - sysfs.SetFilesystem(fs) +func cleanupI2CDevice() { + sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) + sysfs.SetSyscall(&sysfs.NativeSyscall{}) +} - sysfs.SetSyscall(&sysfs.MockSyscall{ - Impl: syscallImplFail, - }) - i, _ := sysfs.NewI2cDevice("/dev/i2c-1") +func initI2CDeviceAddressError() I2cDevice { + fs := sysfs.NewMockFilesystem([]string{dev}) + sysfs.SetFilesystem(fs) + sysfs.SetSyscall(&sysfs.MockSyscall{Impl: syscallImplFail}) + + i, _ := sysfs.NewI2cDevice(dev) return i } func TestI2CAddress(t *testing.T) { c := NewConnection(initI2CDevice(), 0x66) + defer cleanupI2CDevice() gobottest.Assert(t, c.address, 0x66) } func TestI2CClose(t *testing.T) { c := NewConnection(initI2CDevice(), 0x06) + defer cleanupI2CDevice() gobottest.Assert(t, c.Close(), nil) } @@ -74,102 +75,119 @@ func TestI2CRead(t *testing.T) { func TestI2CReadAddressError(t *testing.T) { c := NewConnection(initI2CDeviceAddressError(), 0x06) + defer cleanupI2CDevice() _, err := c.Read([]byte{}) gobottest.Assert(t, err, errors.New("Setting address failed with syscall.Errno operation not permitted")) } func TestI2CWrite(t *testing.T) { c := NewConnection(initI2CDevice(), 0x06) + defer cleanupI2CDevice() i, _ := c.Write([]byte{0x01}) gobottest.Assert(t, i, 1) } func TestI2CWriteAddressError(t *testing.T) { c := NewConnection(initI2CDeviceAddressError(), 0x06) + defer cleanupI2CDevice() _, err := c.Write([]byte{0x01}) gobottest.Assert(t, err, errors.New("Setting address failed with syscall.Errno operation not permitted")) } func TestI2CReadByte(t *testing.T) { c := NewConnection(initI2CDevice(), 0x06) + defer cleanupI2CDevice() v, _ := c.ReadByte() - gobottest.Assert(t, v, uint8(0)) + gobottest.Assert(t, v, uint8(0xFC)) } func TestI2CReadByteAddressError(t *testing.T) { c := NewConnection(initI2CDeviceAddressError(), 0x06) + defer cleanupI2CDevice() _, err := c.ReadByte() gobottest.Assert(t, err, errors.New("Setting address failed with syscall.Errno operation not permitted")) } func TestI2CReadByteData(t *testing.T) { c := NewConnection(initI2CDevice(), 0x06) + defer cleanupI2CDevice() v, _ := c.ReadByteData(0x01) - gobottest.Assert(t, v, uint8(0)) + gobottest.Assert(t, v, uint8(0xFD)) } func TestI2CReadByteDataAddressError(t *testing.T) { c := NewConnection(initI2CDeviceAddressError(), 0x06) + defer cleanupI2CDevice() _, err := c.ReadByteData(0x01) gobottest.Assert(t, err, errors.New("Setting address failed with syscall.Errno operation not permitted")) } func TestI2CReadWordData(t *testing.T) { c := NewConnection(initI2CDevice(), 0x06) + defer cleanupI2CDevice() v, _ := c.ReadWordData(0x01) - gobottest.Assert(t, v, uint16(0)) + gobottest.Assert(t, v, uint16(0xFFFE)) } func TestI2CReadWordDataAddressError(t *testing.T) { c := NewConnection(initI2CDeviceAddressError(), 0x06) + defer cleanupI2CDevice() _, err := c.ReadWordData(0x01) gobottest.Assert(t, err, errors.New("Setting address failed with syscall.Errno operation not permitted")) } func TestI2CWriteByte(t *testing.T) { c := NewConnection(initI2CDevice(), 0x06) + defer cleanupI2CDevice() err := c.WriteByte(0x01) gobottest.Assert(t, err, nil) } func TestI2CWriteByteAddressError(t *testing.T) { c := NewConnection(initI2CDeviceAddressError(), 0x06) + defer cleanupI2CDevice() err := c.WriteByte(0x01) gobottest.Assert(t, err, errors.New("Setting address failed with syscall.Errno operation not permitted")) } func TestI2CWriteByteData(t *testing.T) { c := NewConnection(initI2CDevice(), 0x06) + defer cleanupI2CDevice() err := c.WriteByteData(0x01, 0x01) gobottest.Assert(t, err, nil) } func TestI2CWriteByteDataAddressError(t *testing.T) { c := NewConnection(initI2CDeviceAddressError(), 0x06) + defer cleanupI2CDevice() err := c.WriteByteData(0x01, 0x01) gobottest.Assert(t, err, errors.New("Setting address failed with syscall.Errno operation not permitted")) } func TestI2CWriteWordData(t *testing.T) { c := NewConnection(initI2CDevice(), 0x06) + defer cleanupI2CDevice() err := c.WriteWordData(0x01, 0x01) gobottest.Assert(t, err, nil) } func TestI2CWriteWordDataAddressError(t *testing.T) { c := NewConnection(initI2CDeviceAddressError(), 0x06) + defer cleanupI2CDevice() err := c.WriteWordData(0x01, 0x01) gobottest.Assert(t, err, errors.New("Setting address failed with syscall.Errno operation not permitted")) } func TestI2CWriteBlockData(t *testing.T) { c := NewConnection(initI2CDevice(), 0x06) + defer cleanupI2CDevice() err := c.WriteBlockData(0x01, []byte{0x01, 0x02}) gobottest.Assert(t, err, nil) } func TestI2CWriteBlockDataAddressError(t *testing.T) { c := NewConnection(initI2CDeviceAddressError(), 0x06) + defer cleanupI2CDevice() err := c.WriteBlockData(0x01, []byte{0x01, 0x02}) gobottest.Assert(t, err, errors.New("Setting address failed with syscall.Errno operation not permitted")) } diff --git a/drivers/i2c/i2c_driver.go b/drivers/i2c/i2c_driver.go index b56ba468..85e4c4a5 100644 --- a/drivers/i2c/i2c_driver.go +++ b/drivers/i2c/i2c_driver.go @@ -1,9 +1,11 @@ package i2c import ( + "encoding/binary" "fmt" "strconv" "sync" + "time" "gobot.io/x/gobot" ) @@ -85,7 +87,15 @@ func (d *Driver) Halt() error { return nil } -// Write implements a simple write mechanism to the given register of an i2c device. +// Write writes one byte to the i2c device. +func (d *Driver) WriteByte(val byte) error { + d.mutex.Lock() + defer d.mutex.Unlock() + + return d.connection.WriteByte(val) +} + +// Write implements a simple write mechanism, starting from the given register of an i2c device. func (d *Driver) Write(pin string, val int) error { d.mutex.Lock() defer d.mutex.Unlock() @@ -95,10 +105,77 @@ func (d *Driver) Write(pin string, val int) error { return err } - // TODO: create buffer from size - // currently only one byte value is supported - b := []byte{uint8(val)} - return d.connection.WriteBlockData(uint8(register), b) + if val > 0xFFFF { + buf := make([]byte, 4) + binary.LittleEndian.PutUint32(buf, uint32(val)) + return d.connection.WriteBlockData(register, buf) + } + if val > 0xFF { + return d.connection.WriteWordData(register, uint16(val)) + } + return d.connection.WriteByteData(register, uint8(val)) +} + +// WriteByteData writes the given byte value to the given register of an i2c device. +func (d *Driver) WriteByteData(pin string, val byte) error { + d.mutex.Lock() + defer d.mutex.Unlock() + + register, err := driverParseRegister(pin) + if err != nil { + return err + } + + return d.connection.WriteByteData(register, val) +} + +// WriteWordData writes the given 16 bit value to the given register of an i2c device. +func (d *Driver) WriteWordData(pin string, val uint16) error { + d.mutex.Lock() + defer d.mutex.Unlock() + + register, err := driverParseRegister(pin) + if err != nil { + return err + } + + return d.connection.WriteWordData(register, val) +} + +// WriteBlockData writes the given buffer to the given register of an i2c device. +func (d *Driver) WriteBlockData(pin string, data []byte) error { + d.mutex.Lock() + defer d.mutex.Unlock() + + register, err := driverParseRegister(pin) + if err != nil { + return err + } + + return d.connection.WriteBlockData(uint8(register), data) +} + +// WriteData writes the given buffer to the given register of an i2c device. +// It uses plain write to prevent WriteBlockData(), which is sometimes not supported by adaptor. +func (d *Driver) WriteData(pin string, data []byte) error { + d.mutex.Lock() + defer d.mutex.Unlock() + + register, err := driverParseRegister(pin) + if err != nil { + return err + } + + buf := make([]byte, len(data)+1) + copy(buf[1:], data) + buf[0] = register + + cnt, err := d.connection.Write(buf) + if cnt != len(buf) { + return fmt.Errorf("written count (%d) differ from expected (%d)", cnt, len(buf)) + } + + return err } // Read implements a simple read mechanism from the given register of an i2c device. @@ -111,14 +188,88 @@ func (d *Driver) Read(pin string) (int, error) { return 0, err } - // TODO: create buffer from size - // currently only one byte value is supported - b := []byte{0} - if err := d.connection.ReadBlockData(register, b); err != nil { + val, err := d.connection.ReadByteData(register) + if err != nil { return 0, err } - return int(b[0]), nil + return int(val), nil +} + +// ReadByte reads a byte from the current register of an i2c device. +func (d *Driver) ReadByte() (byte, error) { + d.mutex.Lock() + defer d.mutex.Unlock() + + return d.connection.ReadByte() +} + +// ReadByteData reads a byte from the given register of an i2c device. +func (d *Driver) ReadByteData(pin string) (byte, error) { + d.mutex.Lock() + defer d.mutex.Unlock() + + register, err := driverParseRegister(pin) + if err != nil { + return 0, err + } + + return d.connection.ReadByteData(register) +} + +// ReadWordData reads a 16 bit value starting from the given register of an i2c device. +func (d *Driver) ReadWordData(pin string) (uint16, error) { + d.mutex.Lock() + defer d.mutex.Unlock() + + register, err := driverParseRegister(pin) + if err != nil { + return 0, err + } + + return d.connection.ReadWordData(register) +} + +// ReadBlockData fills the given buffer with reads starting from the given register of an i2c device. +func (d *Driver) ReadBlockData(pin string, data []byte) error { + d.mutex.Lock() + defer d.mutex.Unlock() + + register, err := driverParseRegister(pin) + if err != nil { + return err + } + + return d.connection.ReadBlockData(register, data) +} + +// ReadBlockData fills the given buffer with reads from the given register of an i2c device. +// It uses plain read to prevent ReadBlockData(), which is sometimes not supported by adaptor. +func (d *Driver) ReadData(pin string, data []byte) error { + d.mutex.Lock() + defer d.mutex.Unlock() + + register, err := driverParseRegister(pin) + if err != nil { + return err + } + + if err := d.connection.WriteByte(register); err != nil { + return err + } + + // write process needs some time, so wait at least 5ms before read a value + // when decreasing to much, the check below will fail + time.Sleep(10 * time.Millisecond) + + n, err := d.connection.Read(data) + if err != nil { + return err + } + if n != len(data) { + return fmt.Errorf("Read %v bytes from device by sysfs, expected %v", n, len(data)) + } + return nil } func driverParseRegister(pin string) (uint8, error) { diff --git a/examples/tinkerboard_pcf8583_clock.go b/examples/tinkerboard_pcf8583_clock.go index 897a42d9..4fba0488 100644 --- a/examples/tinkerboard_pcf8583_clock.go +++ b/examples/tinkerboard_pcf8583_clock.go @@ -17,7 +17,7 @@ import ( // Wiring // PWR Tinkerboard: 1 (+3.3V, VCC), 6, 9, 14, 20 (GND) // I2C1 Tinkerboard: 3 (SDA), 5 (SCL) -// PCF8583 DIP package: 1 (OSCI, 50Hz), 2 (OSCO, nc), 3 (A0 - GND), 4 (VSS, +3.3V), 5 (SDA), 6 (SCL), 7 (/INT, nc), 8 (VDD, GND) +// PCF8583 DIP package: 1 (OSCI, 50Hz), 2 (OSCO, nc), 3 (A0 - GND), 4 (VSS, GND), 5 (SDA), 6 (SCL), 7 (/INT, nc), 8 (VDD, +3.3V) func main() { board := tinkerboard.NewAdaptor() pcf := i2c.NewPCF8583Driver(board, i2c.WithBus(1), i2c.WithPCF8583Mode(i2c.PCF8583CtrlModeClock50)) diff --git a/examples/tinkerboard_pcf8583_counter.go b/examples/tinkerboard_pcf8583_counter.go index 2cb53319..52fb8133 100644 --- a/examples/tinkerboard_pcf8583_counter.go +++ b/examples/tinkerboard_pcf8583_counter.go @@ -17,7 +17,7 @@ import ( // Wiring // PWR Tinkerboard: 1 (+3.3V, VCC), 6, 9, 14, 20 (GND) // I2C1 Tinkerboard: 3 (SDA), 5 (SCL) -// PCF8583 DIP package: 1 (OSCI, event), 2 (OSCO, nc), 3 (A0 - GND), 4 (VSS, +3.3V), 5 (SDA), 6 (SCL), 7 (/INT, nc), 8 (VDD, GND) +// PCF8583 DIP package: 1 (OSCI, event), 2 (OSCO, nc), 3 (A0 - GND), 4 (VSS, GND), 5 (SDA), 6 (SCL), 7 (/INT, nc), 8 (VDD, +3.3V) // Note: event can be created by e.g. an debounced button func main() { board := tinkerboard.NewAdaptor() diff --git a/platforms/beaglebone/beaglebone_adaptor_test.go b/platforms/beaglebone/beaglebone_adaptor_test.go index 3ce44ad4..0d23fef6 100644 --- a/platforms/beaglebone/beaglebone_adaptor_test.go +++ b/platforms/beaglebone/beaglebone_adaptor_test.go @@ -82,6 +82,7 @@ func TestBeagleboneAdaptor(t *testing.T) { }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) a, _ := initBBBTestAdaptor() @@ -170,6 +171,7 @@ func TestBeagleboneAdaptor(t *testing.T) { // I2c sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) con, err := a.GetConnection(0xff, 2) gobottest.Assert(t, err, nil) @@ -205,6 +207,7 @@ func TestBeagleboneAnalogReadFileError(t *testing.T) { "/sys/devices/platform/whatever", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) a, _ := initBBBTestAdaptor() @@ -219,6 +222,7 @@ func TestBeagleboneDigitalPinDirectionFileError(t *testing.T) { "/sys/devices/platform/ocp/ocp:P9_12_pinmux/state", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) a, _ := initBBBTestAdaptor() @@ -237,6 +241,7 @@ func TestBeagleboneDigitalPinFinalizeFileError(t *testing.T) { "/sys/devices/platform/ocp/ocp:P9_12_pinmux/state", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) a, _ := initBBBTestAdaptor() diff --git a/platforms/chip/chip_adaptor_test.go b/platforms/chip/chip_adaptor_test.go index be6449fd..2d3b675a 100644 --- a/platforms/chip/chip_adaptor_test.go +++ b/platforms/chip/chip_adaptor_test.go @@ -40,6 +40,7 @@ func initTestChipAdaptor() (*Adaptor, *sysfs.MockFilesystem) { }) sysfs.SetFilesystem(fs) + return a, fs } @@ -64,6 +65,10 @@ func initTestChipProAdaptor() (*Adaptor, *sysfs.MockFilesystem) { return a, fs } +func cleanTestChipAdaptor() { + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) +} + func TestChipAdaptorName(t *testing.T) { a := NewAdaptor() gobottest.Assert(t, strings.HasPrefix(a.Name(), "CHIP"), true) @@ -81,6 +86,7 @@ func TestChipAdaptorBoard(t *testing.T) { func TestAdaptorFinalizeErrorAfterGPIO(t *testing.T) { a, fs := initTestChipAdaptor() + defer cleanTestChipAdaptor() gobottest.Assert(t, a.Connect(), nil) gobottest.Assert(t, a.DigitalWrite("CSID7", 1), nil) @@ -92,6 +98,7 @@ func TestAdaptorFinalizeErrorAfterGPIO(t *testing.T) { func TestAdaptorFinalizeErrorAfterPWM(t *testing.T) { a, fs := initTestChipAdaptor() + defer cleanTestChipAdaptor() gobottest.Assert(t, a.Connect(), nil) gobottest.Assert(t, a.PwmWrite("PWM0", 100), nil) @@ -103,6 +110,7 @@ func TestAdaptorFinalizeErrorAfterPWM(t *testing.T) { func TestChipAdaptorDigitalIO(t *testing.T) { a, fs := initTestChipAdaptor() + defer cleanTestChipAdaptor() a.Connect() a.DigitalWrite("CSID7", 1) @@ -118,6 +126,7 @@ func TestChipAdaptorDigitalIO(t *testing.T) { func TestChipProAdaptorDigitalIO(t *testing.T) { a, fs := initTestChipProAdaptor() + defer cleanTestChipAdaptor() a.Connect() a.DigitalWrite("CSID7", 1) @@ -133,6 +142,7 @@ func TestChipProAdaptorDigitalIO(t *testing.T) { func TestAdaptorDigitalWriteError(t *testing.T) { a, fs := initTestChipAdaptor() + defer cleanTestChipAdaptor() fs.WithWriteError = true err := a.DigitalWrite("CSID7", 1) @@ -141,6 +151,7 @@ func TestAdaptorDigitalWriteError(t *testing.T) { func TestAdaptorDigitalReadWriteError(t *testing.T) { a, fs := initTestChipAdaptor() + defer cleanTestChipAdaptor() fs.WithWriteError = true _, err := a.DigitalRead("CSID7") @@ -156,6 +167,7 @@ func TestChipAdaptorI2c(t *testing.T) { }) sysfs.SetFilesystem(fs) sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) con, err := a.GetConnection(0xff, 1) gobottest.Assert(t, err, nil) @@ -170,6 +182,7 @@ func TestChipAdaptorI2c(t *testing.T) { func TestChipAdaptorInvalidPWMPin(t *testing.T) { a, _ := initTestChipAdaptor() + defer cleanTestChipAdaptor() a.Connect() err := a.PwmWrite("LCD-D2", 42) @@ -181,6 +194,7 @@ func TestChipAdaptorInvalidPWMPin(t *testing.T) { func TestChipAdaptorPWM(t *testing.T) { a, fs := initTestChipAdaptor() + defer cleanTestChipAdaptor() a.Connect() err := a.PwmWrite("PWM0", 100) @@ -205,6 +219,7 @@ func TestChipAdaptorPWM(t *testing.T) { func TestAdaptorPwmWriteError(t *testing.T) { a, fs := initTestChipAdaptor() + defer cleanTestChipAdaptor() fs.WithWriteError = true err := a.PwmWrite("PWM0", 100) @@ -213,6 +228,7 @@ func TestAdaptorPwmWriteError(t *testing.T) { func TestAdaptorPwmReadError(t *testing.T) { a, fs := initTestChipAdaptor() + defer cleanTestChipAdaptor() fs.WithReadError = true err := a.PwmWrite("PWM0", 100) @@ -221,11 +237,13 @@ func TestAdaptorPwmReadError(t *testing.T) { func TestChipDefaultBus(t *testing.T) { a, _ := initTestChipAdaptor() + defer cleanTestChipAdaptor() gobottest.Assert(t, a.GetDefaultBus(), 1) } func TestChipGetConnectionInvalidBus(t *testing.T) { a, _ := initTestChipAdaptor() + defer cleanTestChipAdaptor() _, err := a.GetConnection(0x01, 99) gobottest.Assert(t, err, errors.New("Bus number 99 out of range")) } diff --git a/platforms/dragonboard/dragonboard_adaptor_test.go b/platforms/dragonboard/dragonboard_adaptor_test.go index 6e48a8d8..29448a40 100644 --- a/platforms/dragonboard/dragonboard_adaptor_test.go +++ b/platforms/dragonboard/dragonboard_adaptor_test.go @@ -45,6 +45,7 @@ func TestDragonBoardAdaptorDigitalIO(t *testing.T) { }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) _ = a.DigitalWrite("GPIO_B", 1) gobottest.Assert(t, fs.Files["/sys/class/gpio/gpio12/value"].Contents, "1") @@ -63,7 +64,9 @@ func TestDragonBoardAdaptorI2c(t *testing.T) { "/dev/i2c-1", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) con, err := a.GetConnection(0xff, 1) gobottest.Assert(t, err, nil) @@ -99,6 +102,7 @@ func TestAdaptorFinalizeErrorAfterGPIO(t *testing.T) { }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) gobottest.Assert(t, a.Connect(), nil) gobottest.Assert(t, a.DigitalWrite("GPIO_B", 1), nil) diff --git a/platforms/intel-iot/edison/edison_adaptor_test.go b/platforms/intel-iot/edison/edison_adaptor_test.go index e4555fab..4ff7fc72 100644 --- a/platforms/intel-iot/edison/edison_adaptor_test.go +++ b/platforms/intel-iot/edison/edison_adaptor_test.go @@ -102,8 +102,13 @@ func initTestAdaptor() (*Adaptor, *sysfs.MockFilesystem) { return a, fs } +func cleanTestAdaptor() { + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) +} + func TestEdisonAdaptorName(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() gobottest.Assert(t, strings.HasPrefix(a.Name(), "Edison"), true) a.SetName("NewName") gobottest.Assert(t, a.Name(), "NewName") @@ -111,6 +116,7 @@ func TestEdisonAdaptorName(t *testing.T) { func TestAdaptorConnect(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() gobottest.Assert(t, a.Connect(), nil) gobottest.Assert(t, a.GetDefaultBus(), 6) gobottest.Assert(t, a.Board(), "arduino") @@ -120,6 +126,7 @@ func TestAdaptorConnect(t *testing.T) { func TestAdaptorArduinoSetupFail263(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() delete(fs.Files, "/sys/class/gpio/gpio263/direction") err := a.arduinoSetup() @@ -128,6 +135,7 @@ func TestAdaptorArduinoSetupFail263(t *testing.T) { func TestAdaptorArduinoSetupFail240(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() delete(fs.Files, "/sys/class/gpio/gpio240/direction") err := a.arduinoSetup() @@ -136,6 +144,7 @@ func TestAdaptorArduinoSetupFail240(t *testing.T) { func TestAdaptorArduinoSetupFail111(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() delete(fs.Files, "/sys/kernel/debug/gpio_debug/gpio111/current_pinmux") err := a.arduinoSetup() @@ -144,6 +153,7 @@ func TestAdaptorArduinoSetupFail111(t *testing.T) { func TestAdaptorArduinoSetupFail131(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() delete(fs.Files, "/sys/kernel/debug/gpio_debug/gpio131/current_pinmux") err := a.arduinoSetup() @@ -152,6 +162,7 @@ func TestAdaptorArduinoSetupFail131(t *testing.T) { func TestAdaptorArduinoI2CSetupFailTristate(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() gobottest.Assert(t, a.arduinoSetup(), nil) @@ -162,6 +173,7 @@ func TestAdaptorArduinoI2CSetupFailTristate(t *testing.T) { func TestAdaptorArduinoI2CSetupFail14(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() gobottest.Assert(t, a.arduinoSetup(), nil) delete(fs.Files, "/sys/class/gpio/gpio14/direction") @@ -172,6 +184,7 @@ func TestAdaptorArduinoI2CSetupFail14(t *testing.T) { func TestAdaptorArduinoI2CSetupUnexportFail(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() gobottest.Assert(t, a.arduinoSetup(), nil) delete(fs.Files, "/sys/class/gpio/unexport") @@ -182,6 +195,7 @@ func TestAdaptorArduinoI2CSetupUnexportFail(t *testing.T) { func TestAdaptorArduinoI2CSetupFail236(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() gobottest.Assert(t, a.arduinoSetup(), nil) delete(fs.Files, "/sys/class/gpio/gpio236/direction") @@ -192,6 +206,7 @@ func TestAdaptorArduinoI2CSetupFail236(t *testing.T) { func TestAdaptorArduinoI2CSetupFail28(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() gobottest.Assert(t, a.arduinoSetup(), nil) delete(fs.Files, "/sys/kernel/debug/gpio_debug/gpio28/current_pinmux") @@ -202,6 +217,7 @@ func TestAdaptorArduinoI2CSetupFail28(t *testing.T) { func TestAdaptorConnectArduinoError(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() a.writeFile = func(string, []byte) (int, error) { return 0, errors.New("write error") } @@ -212,6 +228,7 @@ func TestAdaptorConnectArduinoError(t *testing.T) { func TestAdaptorConnectArduinoWriteError(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() fs.WithWriteError = true err := a.Connect() gobottest.Assert(t, strings.Contains(err.Error(), "write error"), true) @@ -219,6 +236,7 @@ func TestAdaptorConnectArduinoWriteError(t *testing.T) { func TestAdaptorConnectSparkfun(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() a.SetBoard("sparkfun") gobottest.Assert(t, a.Connect(), nil) gobottest.Assert(t, a.GetDefaultBus(), 1) @@ -227,6 +245,7 @@ func TestAdaptorConnectSparkfun(t *testing.T) { func TestAdaptorConnectMiniboard(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() a.SetBoard("miniboard") gobottest.Assert(t, a.Connect(), nil) gobottest.Assert(t, a.GetDefaultBus(), 1) @@ -235,16 +254,19 @@ func TestAdaptorConnectMiniboard(t *testing.T) { func TestAdaptorConnectUnknown(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() a.SetBoard("wha") gobottest.Refute(t, a.Connect(), nil) } func TestAdaptorFinalize(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() a.DigitalWrite("3", 1) a.PwmWrite("5", 100) sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) a.GetConnection(0xff, 6) gobottest.Assert(t, a.Finalize(), nil) @@ -255,6 +277,7 @@ func TestAdaptorFinalize(t *testing.T) { func TestAdaptorFinalizeError(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() a.PwmWrite("5", 100) fs.WithWriteError = true @@ -263,6 +286,7 @@ func TestAdaptorFinalizeError(t *testing.T) { func TestAdaptorDigitalIO(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() a.DigitalWrite("13", 1) gobottest.Assert(t, fs.Files["/sys/class/gpio/gpio40/value"].Contents, "1") @@ -289,6 +313,7 @@ func TestAdaptorDigitalPinInFileError(t *testing.T) { "/sys/class/gpio/gpio261/direction", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) a.Connect() @@ -313,6 +338,7 @@ func TestAdaptorDigitalPinInResistorFileError(t *testing.T) { "/sys/class/gpio/gpio261/direction", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) a.Connect() @@ -336,6 +362,7 @@ func TestAdaptorDigitalPinInLevelShifterFileError(t *testing.T) { // "/sys/class/gpio/gpio261/direction", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) a.Connect() @@ -359,6 +386,7 @@ func TestAdaptorDigitalPinInMuxFileError(t *testing.T) { "/sys/class/gpio/gpio261/direction", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) a.Connect() @@ -368,6 +396,7 @@ func TestAdaptorDigitalPinInMuxFileError(t *testing.T) { func TestAdaptorDigitalWriteError(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() fs.WithWriteError = true err := a.DigitalWrite("13", 1) @@ -376,6 +405,7 @@ func TestAdaptorDigitalWriteError(t *testing.T) { func TestAdaptorDigitalReadWriteError(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() fs.WithWriteError = true _, err := a.DigitalRead("13") @@ -384,8 +414,11 @@ func TestAdaptorDigitalReadWriteError(t *testing.T) { func TestAdaptorI2c(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) + con, err := a.GetConnection(0xff, 6) gobottest.Assert(t, err, nil) @@ -399,12 +432,14 @@ func TestAdaptorI2c(t *testing.T) { func TestAdaptorI2cInvalidBus(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() _, err := a.GetConnection(0xff, 3) gobottest.Assert(t, err, errors.New("Unsupported I2C bus")) } func TestAdaptorPwm(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() err := a.PwmWrite("5", 100) gobottest.Assert(t, err, nil) @@ -432,6 +467,7 @@ func TestAdaptorPwmExportError(t *testing.T) { "/sys/class/pwm/pwmchip0/pwm1/enable", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) a.Connect() err := a.PwmWrite("5", 100) @@ -456,6 +492,7 @@ func TestAdaptorPwmEnableError(t *testing.T) { //"/sys/class/pwm/pwmchip0/pwm1/enable", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) a.Connect() err := a.PwmWrite("5", 100) @@ -464,6 +501,7 @@ func TestAdaptorPwmEnableError(t *testing.T) { func TestAdaptorPwmWritePinError(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() a.writeFile = func(string, []byte) (int, error) { return 0, errors.New("write error") @@ -475,6 +513,7 @@ func TestAdaptorPwmWritePinError(t *testing.T) { func TestAdaptorPwmWriteError(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() fs.WithWriteError = true @@ -484,6 +523,7 @@ func TestAdaptorPwmWriteError(t *testing.T) { func TestAdaptorPwmReadError(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() fs.WithReadError = true @@ -493,6 +533,7 @@ func TestAdaptorPwmReadError(t *testing.T) { func TestAdaptorAnalog(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() fs.Files["/sys/bus/iio/devices/iio:device1/in_voltage0_raw"].Contents = "1000\n" i, _ := a.AnalogRead("0") @@ -501,6 +542,7 @@ func TestAdaptorAnalog(t *testing.T) { func TestAdaptorAnalogError(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() a.readFile = func(string) ([]byte, error) { return nil, errors.New("read error") diff --git a/platforms/intel-iot/joule/joule_adaptor_test.go b/platforms/intel-iot/joule/joule_adaptor_test.go index 3a7af420..adb5743c 100644 --- a/platforms/intel-iot/joule/joule_adaptor_test.go +++ b/platforms/intel-iot/joule/joule_adaptor_test.go @@ -88,13 +88,19 @@ func initTestAdaptor() (*Adaptor, *sysfs.MockFilesystem) { "/dev/i2c-0", }) sysfs.SetFilesystem(fs) + fs.Files["/sys/class/pwm/pwmchip0/pwm0/period"].Contents = "5000" a.Connect() return a, fs } +func cleanTestAdaptor() { + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) +} + func TestJouleAdaptorName(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() gobottest.Assert(t, strings.HasPrefix(a.Name(), "Joule"), true) a.SetName("NewName") gobottest.Assert(t, a.Name(), "NewName") @@ -102,12 +108,14 @@ func TestJouleAdaptorName(t *testing.T) { func TestAdaptorConnect(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() gobottest.Assert(t, a.Connect(), nil) gobottest.Assert(t, a.GetDefaultBus(), 0) } func TestAdaptorInvalidBus(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() gobottest.Assert(t, a.Connect(), nil) _, err := a.GetConnection(0xff, 10) @@ -116,10 +124,13 @@ func TestAdaptorInvalidBus(t *testing.T) { func TestAdaptorFinalize(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() a.DigitalWrite("J12_1", 1) a.PwmWrite("J12_26", 100) sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) + gobottest.Assert(t, a.Finalize(), nil) _, err := a.GetConnection(0xff, 0) gobottest.Assert(t, err, nil) @@ -131,6 +142,7 @@ func TestAdaptorFinalize(t *testing.T) { func TestAdaptorDigitalIO(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() a.DigitalWrite("J12_1", 1) gobottest.Assert(t, fs.Files["/sys/class/gpio/gpio451/value"].Contents, "1") @@ -143,6 +155,7 @@ func TestAdaptorDigitalIO(t *testing.T) { func TestAdaptorDigitalWriteError(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() fs.WithWriteError = true err := a.DigitalWrite("13", 1) @@ -151,6 +164,7 @@ func TestAdaptorDigitalWriteError(t *testing.T) { func TestAdaptorDigitalReadWriteError(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() fs.WithWriteError = true _, err := a.DigitalRead("13") @@ -159,8 +173,11 @@ func TestAdaptorDigitalReadWriteError(t *testing.T) { func TestAdaptorI2c(t *testing.T) { a, _ := initTestAdaptor() + defer cleanTestAdaptor() sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) + con, err := a.GetConnection(0xff, 0) gobottest.Assert(t, err, nil) @@ -174,6 +191,7 @@ func TestAdaptorI2c(t *testing.T) { func TestAdaptorPwm(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() err := a.PwmWrite("J12_26", 100) gobottest.Assert(t, err, nil) @@ -188,6 +206,7 @@ func TestAdaptorPwm(t *testing.T) { func TestAdaptorPwmPinExportError(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() delete(fs.Files, "/sys/class/pwm/pwmchip0/export") @@ -197,6 +216,7 @@ func TestAdaptorPwmPinExportError(t *testing.T) { func TestAdaptorPwmPinEnableError(t *testing.T) { a, fs := initTestAdaptor() + defer cleanTestAdaptor() delete(fs.Files, "/sys/class/pwm/pwmchip0/pwm0/enable") diff --git a/platforms/jetson/jetson_adaptor_test.go b/platforms/jetson/jetson_adaptor_test.go index 804afbe6..21058526 100644 --- a/platforms/jetson/jetson_adaptor_test.go +++ b/platforms/jetson/jetson_adaptor_test.go @@ -61,7 +61,9 @@ func TestAdaptorFinalize(t *testing.T) { }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) a.DigitalWrite("3", 1) @@ -81,6 +83,7 @@ func TestAdaptorDigitalIO(t *testing.T) { }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) a.DigitalWrite("7", 1) gobottest.Assert(t, fs.Files["/sys/class/gpio/gpio216/value"].Contents, "1") @@ -106,7 +109,9 @@ func TestAdaptorI2c(t *testing.T) { "/dev/i2c-1", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) con, err := a.GetConnection(0xff, 1) gobottest.Assert(t, err, nil) @@ -128,7 +133,9 @@ func TestAdaptorSPI(t *testing.T) { "/dev/spidev0.1", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) gobottest.Assert(t, a.GetSpiDefaultBus(), 0) gobottest.Assert(t, a.GetSpiDefaultChip(), 0) @@ -145,6 +152,7 @@ func TestAdaptorDigitalPinConcurrency(t *testing.T) { oldProcs := runtime.GOMAXPROCS(0) runtime.GOMAXPROCS(8) + defer runtime.GOMAXPROCS(oldProcs) for retry := 0; retry < 20; retry++ { @@ -162,7 +170,4 @@ func TestAdaptorDigitalPinConcurrency(t *testing.T) { wg.Wait() } - - runtime.GOMAXPROCS(oldProcs) - } diff --git a/platforms/jetson/pwm_pin_test.go b/platforms/jetson/pwm_pin_test.go index aa4846b2..97f734f4 100644 --- a/platforms/jetson/pwm_pin_test.go +++ b/platforms/jetson/pwm_pin_test.go @@ -23,6 +23,7 @@ func TestPwmPin(t *testing.T) { }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) pin, err := NewPWMPin("32") gobottest.Assert(t, pin.Export(), nil) diff --git a/platforms/raspi/raspi_adaptor_test.go b/platforms/raspi/raspi_adaptor_test.go index 44b3bf23..51671cb2 100644 --- a/platforms/raspi/raspi_adaptor_test.go +++ b/platforms/raspi/raspi_adaptor_test.go @@ -99,7 +99,9 @@ func TestAdaptorFinalize(t *testing.T) { }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) a.DigitalWrite("3", 1) a.PwmWrite("7", 255) @@ -112,12 +114,13 @@ func TestAdaptorDigitalPWM(t *testing.T) { a := initTestAdaptor() a.PiBlasterPeriod = 20000000 - gobottest.Assert(t, a.PwmWrite("7", 4), nil) - fs := sysfs.NewMockFilesystem([]string{ "/dev/pi-blaster", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) + + gobottest.Assert(t, a.PwmWrite("7", 4), nil) pin, _ := a.PWMPin("7") period, _ := pin.Period() @@ -155,6 +158,7 @@ func TestAdaptorDigitalIO(t *testing.T) { }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) a.DigitalWrite("7", 1) gobottest.Assert(t, fs.Files["/sys/class/gpio/gpio4/value"].Contents, "1") @@ -180,7 +184,9 @@ func TestAdaptorI2c(t *testing.T) { "/dev/i2c-1", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) con, err := a.GetConnection(0xff, 1) gobottest.Assert(t, err, nil) @@ -202,7 +208,9 @@ func TestAdaptorSPI(t *testing.T) { "/dev/spidev0.1", }) sysfs.SetFilesystem(fs) + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) gobottest.Assert(t, a.GetSpiDefaultBus(), 0) gobottest.Assert(t, a.GetSpiDefaultChip(), 0) @@ -219,6 +227,7 @@ func TestAdaptorDigitalPinConcurrency(t *testing.T) { oldProcs := runtime.GOMAXPROCS(0) runtime.GOMAXPROCS(8) + defer runtime.GOMAXPROCS(oldProcs) for retry := 0; retry < 20; retry++ { @@ -236,9 +245,6 @@ func TestAdaptorDigitalPinConcurrency(t *testing.T) { wg.Wait() } - - runtime.GOMAXPROCS(oldProcs) - } func TestAdaptorPWMPin(t *testing.T) { diff --git a/platforms/tinkerboard/adaptor_test.go b/platforms/tinkerboard/adaptor_test.go index 3970ef73..cafe8581 100644 --- a/platforms/tinkerboard/adaptor_test.go +++ b/platforms/tinkerboard/adaptor_test.go @@ -87,6 +87,10 @@ func initTestTinkerboard(fs *sysfs.MockFilesystem) *Adaptor { return a } +func cleanTestTinkerboard() { + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) +} + func TestTinkerboardName(t *testing.T) { a := NewAdaptor() gobottest.Assert(t, strings.HasPrefix(a.Name(), "Tinker Board"), true) @@ -97,6 +101,7 @@ func TestTinkerboardName(t *testing.T) { func TestTinkerboardDigitalIO(t *testing.T) { fs := gpioFs() a := initTestTinkerboard(fs) + defer cleanTestTinkerboard() a.Connect() a.DigitalWrite("7", 1) @@ -114,6 +119,7 @@ func TestTinkerboardDigitalWriteError(t *testing.T) { fs := gpioFs() fs.WithWriteError = true a := initTestTinkerboard(fs) + defer cleanTestTinkerboard() err := a.DigitalWrite("7", 1) gobottest.Assert(t, err, errors.New("write error")) @@ -123,6 +129,7 @@ func TestTinkerboardDigitalReadWriteError(t *testing.T) { fs := gpioFs() fs.WithWriteError = true a := initTestTinkerboard(fs) + defer cleanTestTinkerboard() _, err := a.DigitalRead("7") gobottest.Assert(t, err, errors.New("write error")) @@ -131,6 +138,7 @@ func TestTinkerboardDigitalReadWriteError(t *testing.T) { func TestTinkerboardInvalidPWMPin(t *testing.T) { fs := pwmFs(t) a := initTestTinkerboard(fs) + defer cleanTestTinkerboard() err := a.PwmWrite("666", 42) gobottest.Refute(t, err, nil) @@ -148,6 +156,7 @@ func TestTinkerboardInvalidPWMPin(t *testing.T) { func TestTinkerboardPwmWrite(t *testing.T) { fs := pwmFs(t) a := initTestTinkerboard(fs) + defer cleanTestTinkerboard() err := a.PwmWrite("33", 100) gobottest.Assert(t, err, nil) @@ -174,6 +183,7 @@ func TestTinkerboardPwmWriteError(t *testing.T) { fs := pwmFs(t) fs.WithWriteError = true a := initTestTinkerboard(fs) + defer cleanTestTinkerboard() err := a.PwmWrite("33", 100) gobottest.Assert(t, strings.Contains(err.Error(), "write error"), true) @@ -183,6 +193,7 @@ func TestTinkerboardPwmWriteReadError(t *testing.T) { fs := pwmFs(t) fs.WithReadError = true a := initTestTinkerboard(fs) + defer cleanTestTinkerboard() err := a.PwmWrite("33", 100) gobottest.Assert(t, strings.Contains(err.Error(), "read error"), true) @@ -192,6 +203,7 @@ func TestTinkerboardSetPeriod(t *testing.T) { // arrange fs := pwmFs(t) a := initTestTinkerboard(fs) + defer cleanTestTinkerboard() newPeriod := uint32(2550000) // act @@ -232,7 +244,9 @@ func TestTinkerboardSetPeriod(t *testing.T) { func TestTinkerboardI2c(t *testing.T) { fs := i2cFs() a := initTestTinkerboard(fs) + defer cleanTestTinkerboard() sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) con, err := a.GetConnection(0xff, 1) gobottest.Assert(t, err, nil) @@ -259,6 +273,7 @@ func TestTinkerboardGetConnectionInvalidBus(t *testing.T) { func TestTinkerboardFinalizeErrorAfterGPIO(t *testing.T) { fs := gpioFs() a := initTestTinkerboard(fs) + defer cleanTestTinkerboard() gobottest.Assert(t, a.Connect(), nil) gobottest.Assert(t, a.DigitalWrite("7", 1), nil) @@ -272,6 +287,7 @@ func TestTinkerboardFinalizeErrorAfterGPIO(t *testing.T) { func TestTinkerboardFinalizeErrorAfterPWM(t *testing.T) { fs := pwmFs(t) a := initTestTinkerboard(fs) + defer cleanTestTinkerboard() gobottest.Assert(t, a.Connect(), nil) gobottest.Assert(t, a.PwmWrite("33", 1), nil) diff --git a/platforms/upboard/up2/adaptor_test.go b/platforms/upboard/up2/adaptor_test.go index 3bcba7d0..ef4b055a 100644 --- a/platforms/upboard/up2/adaptor_test.go +++ b/platforms/upboard/up2/adaptor_test.go @@ -46,6 +46,10 @@ func initTestUP2Adaptor() (*Adaptor, *sysfs.MockFilesystem) { return a, fs } +func cleanTestUP2Adaptor() { + defer sysfs.SetFilesystem(&sysfs.NativeFilesystem{}) +} + func TestUP2AdaptorName(t *testing.T) { a := NewAdaptor() gobottest.Assert(t, strings.HasPrefix(a.Name(), "UP2"), true) @@ -55,6 +59,7 @@ func TestUP2AdaptorName(t *testing.T) { func TestUP2AdaptorDigitalIO(t *testing.T) { a, fs := initTestUP2Adaptor() + defer cleanTestUP2Adaptor() a.Connect() a.DigitalWrite("7", 1) @@ -76,6 +81,7 @@ func TestUP2AdaptorDigitalIO(t *testing.T) { func TestAdaptorDigitalWriteError(t *testing.T) { a, fs := initTestUP2Adaptor() + defer cleanTestUP2Adaptor() fs.WithWriteError = true err := a.DigitalWrite("7", 1) @@ -84,6 +90,7 @@ func TestAdaptorDigitalWriteError(t *testing.T) { func TestAdaptorDigitalReadWriteError(t *testing.T) { a, fs := initTestUP2Adaptor() + defer cleanTestUP2Adaptor() fs.WithWriteError = true _, err := a.DigitalRead("7") @@ -97,6 +104,7 @@ func TestUP2AdaptorI2c(t *testing.T) { }) sysfs.SetFilesystem(fs) sysfs.SetSyscall(&sysfs.MockSyscall{}) + defer sysfs.SetSyscall(&sysfs.NativeSyscall{}) con, err := a.GetConnection(0xff, 5) gobottest.Assert(t, err, nil) @@ -126,6 +134,7 @@ func TestAdaptorSPI(t *testing.T) { func TestUP2AdaptorInvalidPWMPin(t *testing.T) { a, _ := initTestUP2Adaptor() + defer cleanTestUP2Adaptor() a.Connect() err := a.PwmWrite("666", 42) @@ -143,6 +152,7 @@ func TestUP2AdaptorInvalidPWMPin(t *testing.T) { func TestUP2AdaptorPWM(t *testing.T) { a, fs := initTestUP2Adaptor() + defer cleanTestUP2Adaptor() err := a.PwmWrite("32", 100) gobottest.Assert(t, err, nil) @@ -166,6 +176,7 @@ func TestUP2AdaptorPWM(t *testing.T) { func TestUP2AdaptorPwmWriteError(t *testing.T) { a, fs := initTestUP2Adaptor() + defer cleanTestUP2Adaptor() fs.WithWriteError = true err := a.PwmWrite("32", 100) @@ -174,6 +185,7 @@ func TestUP2AdaptorPwmWriteError(t *testing.T) { func TestUP2AdaptorPwmReadError(t *testing.T) { a, fs := initTestUP2Adaptor() + defer cleanTestUP2Adaptor() fs.WithReadError = true err := a.PwmWrite("32", 100) @@ -182,17 +194,20 @@ func TestUP2AdaptorPwmReadError(t *testing.T) { func TestUP2I2CDefaultBus(t *testing.T) { a, _ := initTestUP2Adaptor() + defer cleanTestUP2Adaptor() gobottest.Assert(t, a.GetDefaultBus(), 5) } func TestUP2GetConnectionInvalidBus(t *testing.T) { a, _ := initTestUP2Adaptor() + defer cleanTestUP2Adaptor() _, err := a.GetConnection(0x01, 99) gobottest.Assert(t, err, errors.New("Bus number 99 out of range")) } func TestUP2FinalizeErrorAfterGPIO(t *testing.T) { a, fs := initTestUP2Adaptor() + defer cleanTestUP2Adaptor() gobottest.Assert(t, a.Connect(), nil) gobottest.Assert(t, a.DigitalWrite("7", 1), nil) @@ -205,6 +220,7 @@ func TestUP2FinalizeErrorAfterGPIO(t *testing.T) { func TestUP2FinalizeErrorAfterPWM(t *testing.T) { a, fs := initTestUP2Adaptor() + defer cleanTestUP2Adaptor() gobottest.Assert(t, a.Connect(), nil) gobottest.Assert(t, a.PwmWrite("32", 1), nil) diff --git a/sysfs/README.md b/sysfs/README.md new file mode 100644 index 00000000..58b08886 --- /dev/null +++ b/sysfs/README.md @@ -0,0 +1,39 @@ +# sysfs + +## I2C + +### Byte order + +All common libraries (smbus, digispark, firmata, i2cget) read and write I2C data in the order LSByte, MSByte. +Often the devices store its bytes in the reverse order and therefor needs to be swapped after reading. + +### Linux syscall implementation + +In general there are different ioctl features for I2C + +* IOCTL I2C_RDWR, needs "I2C_FUNC_I2C" +* IOCTL SMBUS, needs "I2C_FUNC_SMBUS.." +* SYSFS I/O +* call of "i2c_smbus_* methods" + +>The possible functions should be checked before by "I2C_FUNCS". + +for further reading see: + +* https://www.kernel.org/doc/Documentation/i2c/dev-interface +* https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/i2c-dev.h#L42 +* https://stackoverflow.com/questions/9974592/i2c-slave-ioctl-purpose (good for understanding, but there are some small errors in the provided example) + +>Qotation from kernel.org: "If possible, use the provided i2c_smbus_* methods described below instead of issuing direct ioctls." We do not do this at the moment, instead we using the "IOCTL SMBUS". + +Because the syscall needs uintptr in Go, there are some known pitfalls with that. Following documents could be helpful: + +* https://go101.org/article/unsafe.html +* https://stackoverflow.com/questions/51187973/how-to-create-an-array-or-a-slice-from-an-array-unsafe-pointer-in-golang +* https://stackoverflow.com/questions/59042646/whats-the-difference-between-uint-and-uintptr-in-golang +* https://go.dev/play/p/Wd7hWn9Zsu +* for go vet false positives, see: https://github.com/golang/go/issues/41205 + +Basically by convert to an uintptr, which is than just a number to an object existing at the moment of creation without +any other reference, the garbage collector will possible destroy the original object. Therefor uintptr should be avoided +as long as possible. diff --git a/sysfs/digital_pin_bench_test.go b/sysfs/digital_pin_bench_test.go index 3a6a9ef9..d033d4da 100644 --- a/sysfs/digital_pin_bench_test.go +++ b/sysfs/digital_pin_bench_test.go @@ -1,6 +1,8 @@ package sysfs -import "testing" +import ( + "testing" +) func BenchmarkDigitalRead(b *testing.B) { fs := NewMockFilesystem([]string{ @@ -11,6 +13,8 @@ func BenchmarkDigitalRead(b *testing.B) { }) SetFilesystem(fs) + defer SetFilesystem(&NativeFilesystem{}) + pin := NewDigitalPin(10) pin.Write(1) diff --git a/sysfs/digital_pin_test.go b/sysfs/digital_pin_test.go index 697c420b..fa8b49cf 100644 --- a/sysfs/digital_pin_test.go +++ b/sysfs/digital_pin_test.go @@ -18,6 +18,7 @@ func TestDigitalPin(t *testing.T) { }) SetFilesystem(fs) + defer SetFilesystem(&NativeFilesystem{}) pin := NewDigitalPin(10, "custom") gobottest.Assert(t, pin.pin, "10") @@ -94,6 +95,7 @@ func TestDigitalPinExportError(t *testing.T) { }) SetFilesystem(fs) + defer SetFilesystem(&NativeFilesystem{}) pin := NewDigitalPin(10, "custom") writeFile = func(File, []byte) (int, error) { @@ -113,6 +115,7 @@ func TestDigitalPinUnexportError(t *testing.T) { }) SetFilesystem(fs) + defer SetFilesystem(&NativeFilesystem{}) pin := NewDigitalPin(10, "custom") writeFile = func(File, []byte) (int, error) { diff --git a/sysfs/i2c_device.go b/sysfs/i2c_device.go index 61edfef9..90aa9f74 100644 --- a/sysfs/i2c_device.go +++ b/sysfs/i2c_device.go @@ -30,7 +30,7 @@ const ( I2C_FUNC_SMBUS_WRITE_WORD_DATA = 0x00400000 I2C_FUNC_SMBUS_READ_BLOCK_DATA = 0x01000000 I2C_FUNC_SMBUS_WRITE_BLOCK_DATA = 0x02000000 - // Transaction types + // Transaction types, also declare the transfer size (register and data) I2C_SMBUS_BYTE = 1 I2C_SMBUS_BYTE_DATA = 2 I2C_SMBUS_WORD_DATA = 3 @@ -45,7 +45,7 @@ type i2cSmbusIoctlData struct { readWrite byte command byte size uint32 - data uintptr + data unsafe.Pointer } type i2cDevice struct { @@ -69,12 +69,7 @@ func NewI2cDevice(location string) (d *i2cDevice, err error) { } func (d *i2cDevice) queryFunctionality() error { - _, _, errno := Syscall( - syscall.SYS_IOCTL, - d.file.Fd(), - I2C_FUNCS, - uintptr(unsafe.Pointer(&d.funcs)), - ) + _, _, errno := Syscall(syscall.SYS_IOCTL, d.file, I2C_FUNCS, unsafe.Pointer(&d.funcs)) if errno != 0 { return fmt.Errorf("Querying functionality failed with syscall.Errno %v", errno) @@ -82,13 +77,10 @@ func (d *i2cDevice) queryFunctionality() error { return nil } +// SetAddress sets the address of the i2c device to use. func (d *i2cDevice) SetAddress(address int) error { - _, _, errno := Syscall( - syscall.SYS_IOCTL, - d.file.Fd(), - I2C_SLAVE, - uintptr(byte(address)), - ) + // for go vet false positives, see: https://github.com/golang/go/issues/41205 + _, _, errno := Syscall(syscall.SYS_IOCTL, d.file, I2C_SLAVE, unsafe.Pointer(uintptr(byte(address)))) if errno != 0 { return fmt.Errorf("Setting address failed with syscall.Errno %v", errno) @@ -96,45 +88,52 @@ func (d *i2cDevice) SetAddress(address int) error { return nil } +// Close closes the character device file. func (d *i2cDevice) Close() error { return d.file.Close() } -func (d *i2cDevice) ReadByte() (val byte, err error) { +// ReadByte reads a byte from the current register of an i2c device. +func (d *i2cDevice) ReadByte() (byte, error) { if d.funcs&I2C_FUNC_SMBUS_READ_BYTE == 0 { return 0, fmt.Errorf("SMBus read byte not supported") } - var data uint8 - err = d.smbusAccess(I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, uintptr(unsafe.Pointer(&data))) + var data uint8 = 0xFC // set value for debugging purposes + err := d.smbusAccess(I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, unsafe.Pointer(&data)) return data, err } +// ReadByteData reads a byte from the given register of an i2c device. func (d *i2cDevice) ReadByteData(reg uint8) (val uint8, err error) { if d.funcs&I2C_FUNC_SMBUS_READ_BYTE_DATA == 0 { return 0, fmt.Errorf("SMBus read byte data not supported") } - var data uint8 - err = d.smbusAccess(I2C_SMBUS_READ, reg, I2C_SMBUS_BYTE_DATA, uintptr(unsafe.Pointer(&data))) + var data uint8 = 0xFD // set value for debugging purposes + err = d.smbusAccess(I2C_SMBUS_READ, reg, I2C_SMBUS_BYTE_DATA, unsafe.Pointer(&data)) return data, err } +// ReadWordData reads a 16 bit value starting from the given register of an i2c device. func (d *i2cDevice) ReadWordData(reg uint8) (val uint16, err error) { if d.funcs&I2C_FUNC_SMBUS_READ_WORD_DATA == 0 { return 0, fmt.Errorf("SMBus read word data not supported") } - var data uint16 - err = d.smbusAccess(I2C_SMBUS_READ, reg, I2C_SMBUS_WORD_DATA, uintptr(unsafe.Pointer(&data))) + var data uint16 = 0xFFFE // set value for debugging purposes + err = d.smbusAccess(I2C_SMBUS_READ, reg, I2C_SMBUS_WORD_DATA, unsafe.Pointer(&data)) return data, err } +// ReadBlockData fills the given buffer with reads starting from the given register of an i2c device. func (d *i2cDevice) ReadBlockData(reg uint8, data []byte) error { - if len(data) > 32 { + lenWithReg := len(data) + 1 + if lenWithReg > 33 { return fmt.Errorf("Reading blocks larger than 32 bytes (%v) not supported", len(data)) } + data[0] = 0xFF // set value for debugging purposes if d.funcs&I2C_FUNC_SMBUS_READ_BLOCK_DATA == 0 { if i2cDeviceDebug { log.Printf("SMBus read block data not supported, use fallback\n") @@ -142,37 +141,42 @@ func (d *i2cDevice) ReadBlockData(reg uint8, data []byte) error { return d.readBlockDataFallback(reg, data) } - return d.smbusAccess(I2C_SMBUS_READ, reg, I2C_SMBUS_BLOCK_DATA, uintptr(unsafe.Pointer(&data))) + return d.smbusAccess(I2C_SMBUS_READ, reg, uint32(lenWithReg), unsafe.Pointer(&data[0])) } +// WriteByte writes the given byte value to the current register of an i2c device. func (d *i2cDevice) WriteByte(val byte) error { if d.funcs&I2C_FUNC_SMBUS_WRITE_BYTE == 0 { return fmt.Errorf("SMBus write byte not supported") } - return d.smbusAccess(I2C_SMBUS_WRITE, val, I2C_SMBUS_BYTE, uintptr(0)) + return d.smbusAccess(I2C_SMBUS_WRITE, val, I2C_SMBUS_BYTE, nil) } +// WriteByteData writes the given byte value to the given register of an i2c device. func (d *i2cDevice) WriteByteData(reg uint8, val uint8) error { if d.funcs&I2C_FUNC_SMBUS_WRITE_BYTE_DATA == 0 { return fmt.Errorf("SMBus write byte data not supported") } var data = val - return d.smbusAccess(I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, uintptr(unsafe.Pointer(&data))) + return d.smbusAccess(I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, unsafe.Pointer(&data)) } +// WriteWordData writes the given 16 bit value starting from the given register of an i2c device. func (d *i2cDevice) WriteWordData(reg uint8, val uint16) error { if d.funcs&I2C_FUNC_SMBUS_WRITE_WORD_DATA == 0 { return fmt.Errorf("SMBus write word data not supported") } var data = val - return d.smbusAccess(I2C_SMBUS_WRITE, reg, I2C_SMBUS_WORD_DATA, uintptr(unsafe.Pointer(&data))) + return d.smbusAccess(I2C_SMBUS_WRITE, reg, I2C_SMBUS_WORD_DATA, unsafe.Pointer(&data)) } +// WriteBlockData writes the given buffer starting from the given register of an i2c device. func (d *i2cDevice) WriteBlockData(reg uint8, data []byte) error { - if len(data) > 32 { + lenWithReg := len(data) + 1 + if lenWithReg > 33 { return fmt.Errorf("Writing blocks larger than 32 bytes (%v) not supported", len(data)) } @@ -183,7 +187,7 @@ func (d *i2cDevice) WriteBlockData(reg uint8, data []byte) error { return d.writeBlockDataFallback(reg, data) } - return d.smbusAccess(I2C_SMBUS_WRITE, reg, I2C_SMBUS_BLOCK_DATA, uintptr(unsafe.Pointer(&data))) + return d.smbusAccess(I2C_SMBUS_WRITE, reg, uint32(lenWithReg), unsafe.Pointer(&data[0])) } // Read implements the io.ReadWriteCloser method by direct I2C read operations. @@ -196,20 +200,15 @@ func (d *i2cDevice) Write(b []byte) (n int, err error) { return d.file.Write(b) } -func (d *i2cDevice) smbusAccess(readWrite byte, command byte, size uint32, data uintptr) error { - smbus := &i2cSmbusIoctlData{ +func (d *i2cDevice) smbusAccess(readWrite byte, command byte, size uint32, dataStart unsafe.Pointer) error { + smbus := i2cSmbusIoctlData{ readWrite: readWrite, command: command, size: size, - data: data, + data: dataStart, // the reflected value of unsafePointer equals uintptr(dataStart), } - _, _, errno := Syscall( - syscall.SYS_IOCTL, - d.file.Fd(), - I2C_SMBUS, - uintptr(unsafe.Pointer(smbus)), - ) + _, _, errno := Syscall(syscall.SYS_IOCTL, d.file, I2C_SMBUS, unsafe.Pointer(&smbus)) if errno != 0 { return fmt.Errorf("Failed with syscall.Errno %v", errno) diff --git a/sysfs/i2c_device_test.go b/sysfs/i2c_device_test.go index f65e22ae..7936ebfe 100644 --- a/sysfs/i2c_device_test.go +++ b/sysfs/i2c_device_test.go @@ -10,340 +10,507 @@ import ( "gobot.io/x/gobot/gobottest" ) -func TestNewI2cDeviceClose(t *testing.T) { - fs := NewMockFilesystem([]string{ - "/dev/i2c-1", - }) +const dev = "/dev/i2c-1" - SetFilesystem(fs) - SetSyscall(&MockSyscall{}) - - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - gobottest.Assert(t, i.Close(), nil) +func initTestI2cDeviceWithMockedSys() (*i2cDevice, *MockSyscall) { + SetFilesystem(NewMockFilesystem([]string{dev})) + msc := &MockSyscall{} + SetSyscall(msc) + d, err := NewI2cDevice(dev) + if err != nil { + panic(err) + } + return d, msc } -func TestNewI2cDeviceQueryFuncError(t *testing.T) { - fs := NewMockFilesystem([]string{ - "/dev/i2c-1", - }) - SetFilesystem(fs) - - SetSyscall(&MockSyscall{ - Impl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { - return 0, 0, 1 - }, - }) - - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, errors.New("Querying functionality failed with syscall.Errno operation not permitted")) +func cleanTestI2cDevice() { + defer SetFilesystem(&NativeFilesystem{}) + defer SetSyscall(&NativeSyscall{}) } func TestNewI2cDevice(t *testing.T) { - fs := NewMockFilesystem([]string{}) - SetFilesystem(fs) - SetSyscall(&MockSyscall{}) - - i, err := NewI2cDevice(os.DevNull) - gobottest.Assert(t, err.Error(), " : /dev/null: No such file.") - - fs = NewMockFilesystem([]string{ - "/dev/i2c-1", - }) - SetFilesystem(fs) - - i, err = NewI2cDevice("/dev/i2c-1") - gobottest.Assert(t, err, nil) - - SetSyscall(&MockSyscall{}) - - i, err = NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - gobottest.Assert(t, i.SetAddress(0xff), nil) - - buf := []byte{0x01, 0x02, 0x03} - - n, err := i.Write(buf) - - gobottest.Assert(t, n, len(buf)) - gobottest.Assert(t, err, nil) - - buf = make([]byte, 4) - - n, err = i.Read(buf) - - gobottest.Assert(t, n, 3) - gobottest.Assert(t, err, nil) -} - -func TestNewI2cDeviceReadByte(t *testing.T) { - fs := NewMockFilesystem([]string{ - "/dev/i2c-1", - }) - SetFilesystem(fs) - - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - i.funcs = I2C_FUNC_SMBUS_READ_BYTE - - val, e := i.ReadByte() - gobottest.Assert(t, val, byte(0)) - gobottest.Assert(t, e, nil) -} - -func TestNewI2cDeviceReadByteError(t *testing.T) { - fs := NewMockFilesystem([]string{ - "/dev/i2c-1", - }) - SetFilesystem(fs) - - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - SetSyscall(&MockSyscall{ - Impl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { - return 0, 0, 1 + var tests = map[string]struct { + dev string + wantErr string + }{ + "ok": { + dev: dev, }, - }) - - i.SetAddress(0xff) - i.funcs = I2C_FUNC_SMBUS_READ_BYTE - - _, e := i.ReadByte() - gobottest.Refute(t, e, nil) + "null": { + dev: os.DevNull, + wantErr: " : /dev/null: No such file.", + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // arrange + SetFilesystem(NewMockFilesystem([]string{dev})) + defer SetFilesystem(&NativeFilesystem{}) + SetSyscall(&MockSyscall{}) + defer SetSyscall(&NativeSyscall{}) + // act + i, err := NewI2cDevice(tc.dev) + var _ i2c.I2cDevice = i + if tc.wantErr != "" { + gobottest.Refute(t, err, nil) + gobottest.Assert(t, err.Error(), tc.wantErr) + } else { + gobottest.Assert(t, err, nil) + } + }) + } } -func TestNewI2cDeviceReadByteNotSupported(t *testing.T) { - SetSyscall(&MockSyscall{}) - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - - _, err = i.ReadByte() - gobottest.Assert(t, err.Error(), "SMBus read byte not supported") +func TestNewI2cDeviceQueryFuncError(t *testing.T) { + // arrange + SetFilesystem(NewMockFilesystem([]string{dev})) + defer SetFilesystem(&NativeFilesystem{}) + SetSyscall(&MockSyscall{Impl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 }}) + defer SetSyscall(&NativeSyscall{}) + // act + _, err := NewI2cDevice(dev) + // assert + gobottest.Assert(t, err, errors.New("Querying functionality failed with syscall.Errno operation not permitted")) } -func TestNewI2cDeviceWriteByte(t *testing.T) { - fs := NewMockFilesystem([]string{ - "/dev/i2c-1", - }) - SetFilesystem(fs) - - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - i.funcs = I2C_FUNC_SMBUS_WRITE_BYTE - - e := i.WriteByte(0x01) - gobottest.Assert(t, e, nil) +func TestClose(t *testing.T) { + // arrange + d, _ := initTestI2cDeviceWithMockedSys() + defer cleanTestI2cDevice() + // act & assert + gobottest.Assert(t, d.Close(), nil) } -func TestNewI2cDeviceWriteByteNotSupported(t *testing.T) { - SetSyscall(&MockSyscall{}) - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - +func TestSetAddress(t *testing.T) { + // arrange + d, msc := initTestI2cDeviceWithMockedSys() + defer cleanTestI2cDevice() + // act + err := d.SetAddress(0xff) + // assert gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - - err = i.WriteByte(0x01) - gobottest.Assert(t, err.Error(), "SMBus write byte not supported") + gobottest.Assert(t, msc.devAddress, uintptr(0xff)) } -func TestNewI2cDeviceReadByteData(t *testing.T) { - fs := NewMockFilesystem([]string{ - "/dev/i2c-1", - }) - SetFilesystem(fs) - - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - i.funcs = I2C_FUNC_SMBUS_READ_BYTE_DATA - - v, e := i.ReadByteData(0x01) - gobottest.Assert(t, v, byte(0)) - gobottest.Assert(t, e, nil) +func TestWriteRead(t *testing.T) { + // arrange + d, _ := initTestI2cDeviceWithMockedSys() + defer cleanTestI2cDevice() + wbuf := []byte{0x01, 0x02, 0x03} + rbuf := make([]byte, 4) + // act + wn, werr := d.Write(wbuf) + rn, rerr := d.Read(rbuf) + // assert + gobottest.Assert(t, werr, nil) + gobottest.Assert(t, rerr, nil) + gobottest.Assert(t, wn, len(wbuf)) + gobottest.Assert(t, rn, len(wbuf)) // will read only the written values + gobottest.Assert(t, wbuf, rbuf[:len(wbuf)]) } -func TestNewI2cDeviceReadByteDataNotSupported(t *testing.T) { - SetSyscall(&MockSyscall{}) - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - - _, err = i.ReadByteData(0x01) - gobottest.Assert(t, err.Error(), "SMBus read byte data not supported") +func TestReadByte(t *testing.T) { + var tests = map[string]struct { + funcs uint64 + syscallImpl func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) + wantErr string + }{ + "read_byte_ok": { + funcs: I2C_FUNC_SMBUS_READ_BYTE, + }, + "error_syscall": { + funcs: I2C_FUNC_SMBUS_READ_BYTE, + syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 }, + wantErr: "Failed with syscall.Errno operation not permitted", + }, + "error_not_supported": { + wantErr: "SMBus read byte not supported", + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // arrange + d, msc := initTestI2cDeviceWithMockedSys() + defer cleanTestI2cDevice() + msc.Impl = tc.syscallImpl + d.funcs = tc.funcs + const want = byte(5) + msc.dataSlice = []byte{want} + // act + got, err := d.ReadByte() + // assert + if tc.wantErr != "" { + gobottest.Refute(t, err, nil) + gobottest.Assert(t, err.Error(), tc.wantErr) + } else { + gobottest.Assert(t, err, nil) + gobottest.Assert(t, got, want) + gobottest.Assert(t, msc.lastFile, d.file) + gobottest.Assert(t, msc.lastSignal, uintptr(I2C_SMBUS)) + gobottest.Assert(t, msc.smbus.readWrite, byte(I2C_SMBUS_READ)) + gobottest.Assert(t, msc.smbus.command, byte(0)) // register is set to 0 in that case + gobottest.Assert(t, msc.smbus.size, uint32(I2C_SMBUS_BYTE)) + } + }) + } } -func TestNewI2cDeviceWriteByteData(t *testing.T) { - fs := NewMockFilesystem([]string{ - "/dev/i2c-1", - }) - SetFilesystem(fs) - - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - i.funcs = I2C_FUNC_SMBUS_WRITE_BYTE_DATA - - e := i.WriteByteData(0x01, 0x02) - gobottest.Assert(t, e, nil) +func TestReadByteData(t *testing.T) { + var tests = map[string]struct { + funcs uint64 + syscallImpl func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) + wantErr string + }{ + "read_byte_data_ok": { + funcs: I2C_FUNC_SMBUS_READ_BYTE_DATA, + }, + "error_syscall": { + funcs: I2C_FUNC_SMBUS_READ_BYTE_DATA, + syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 }, + wantErr: "Failed with syscall.Errno operation not permitted", + }, + "error_not_supported": { + wantErr: "SMBus read byte data not supported", + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // arrange + d, msc := initTestI2cDeviceWithMockedSys() + defer cleanTestI2cDevice() + msc.Impl = tc.syscallImpl + d.funcs = tc.funcs + const ( + reg = byte(0x01) + want = byte(0x02) + ) + msc.dataSlice = []byte{want} + // act + got, err := d.ReadByteData(reg) + // assert + if tc.wantErr != "" { + gobottest.Refute(t, err, nil) + gobottest.Assert(t, err.Error(), tc.wantErr) + } else { + gobottest.Assert(t, err, nil) + gobottest.Assert(t, got, want) + gobottest.Assert(t, msc.lastFile, d.file) + gobottest.Assert(t, msc.lastSignal, uintptr(I2C_SMBUS)) + gobottest.Assert(t, msc.smbus.readWrite, byte(I2C_SMBUS_READ)) + gobottest.Assert(t, msc.smbus.command, reg) + gobottest.Assert(t, msc.smbus.size, uint32(I2C_SMBUS_BYTE_DATA)) + } + }) + } } -func TestNewI2cDeviceWriteByteDataNotSupported(t *testing.T) { - SetSyscall(&MockSyscall{}) - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - - err = i.WriteByteData(0x01, 0x01) - gobottest.Assert(t, err.Error(), "SMBus write byte data not supported") +func TestReadWordData(t *testing.T) { + var tests = map[string]struct { + funcs uint64 + syscallImpl func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) + wantErr string + }{ + "read_word_data_ok": { + funcs: I2C_FUNC_SMBUS_READ_WORD_DATA, + }, + "error_syscall": { + funcs: I2C_FUNC_SMBUS_READ_WORD_DATA, + syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 }, + wantErr: "Failed with syscall.Errno operation not permitted", + }, + "error_not_supported": { + wantErr: "SMBus read word data not supported", + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // arrange + d, msc := initTestI2cDeviceWithMockedSys() + defer cleanTestI2cDevice() + msc.Impl = tc.syscallImpl + d.funcs = tc.funcs + const ( + reg = byte(0x02) + msbyte = byte(0xD4) + lsbyte = byte(0x31) + want = uint16(54321) + ) + // all common drivers read LSByte first + msc.dataSlice = []byte{lsbyte, msbyte} + // act + got, err := d.ReadWordData(reg) + // assert + if tc.wantErr != "" { + gobottest.Refute(t, err, nil) + gobottest.Assert(t, err.Error(), tc.wantErr) + } else { + gobottest.Assert(t, err, nil) + gobottest.Assert(t, got, want) + gobottest.Assert(t, msc.lastFile, d.file) + gobottest.Assert(t, msc.lastSignal, uintptr(I2C_SMBUS)) + gobottest.Assert(t, msc.smbus.readWrite, byte(I2C_SMBUS_READ)) + gobottest.Assert(t, msc.smbus.command, reg) + gobottest.Assert(t, msc.smbus.size, uint32(I2C_SMBUS_WORD_DATA)) + } + }) + } } -func TestNewI2cDeviceReadWordData(t *testing.T) { - fs := NewMockFilesystem([]string{ - "/dev/i2c-1", - }) - SetFilesystem(fs) - - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - i.funcs = I2C_FUNC_SMBUS_READ_WORD_DATA - - v, e := i.ReadWordData(0x01) - gobottest.Assert(t, v, uint16(0)) - gobottest.Assert(t, e, nil) +func TestReadBlockData(t *testing.T) { + var tests = map[string]struct { + funcs uint64 + syscallImpl func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) + wantErr string + }{ + "read_block_data_ok": { + funcs: I2C_FUNC_SMBUS_READ_BLOCK_DATA, + }, + "error_syscall": { + funcs: I2C_FUNC_SMBUS_READ_BLOCK_DATA, + syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 }, + wantErr: "Failed with syscall.Errno operation not permitted", + }, + "error_from_used_fallback_if_not_supported": { + wantErr: "Read 1 bytes from device by sysfs, expected 3", + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // arrange + d, msc := initTestI2cDeviceWithMockedSys() + defer cleanTestI2cDevice() + msc.Impl = tc.syscallImpl + d.funcs = tc.funcs + const ( + reg = byte(0x03) + wantByte0 = byte(0x11) + wantByte1 = byte(0x22) + wantByte2 = byte(0x33) + ) + msc.dataSlice = []byte{wantByte0, wantByte1, wantByte2} + wantSize := uint32(len(msc.dataSlice) + 1) // register is also part of send data + buf := []byte{17, 28, 39} + // act + err := d.ReadBlockData(reg, buf) + // assert + if tc.wantErr != "" { + gobottest.Refute(t, err, nil) + gobottest.Assert(t, err.Error(), tc.wantErr) + } else { + gobottest.Assert(t, err, nil) + gobottest.Assert(t, buf, msc.dataSlice) + gobottest.Assert(t, msc.lastFile, d.file) + gobottest.Assert(t, msc.lastSignal, uintptr(I2C_SMBUS)) + gobottest.Assert(t, msc.smbus.readWrite, byte(I2C_SMBUS_READ)) + gobottest.Assert(t, msc.smbus.command, reg) + gobottest.Assert(t, msc.smbus.size, wantSize) + } + }) + } } -func TestNewI2cDeviceReadWordDataNotSupported(t *testing.T) { - SetSyscall(&MockSyscall{}) - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - - _, err = i.ReadWordData(0x01) - gobottest.Assert(t, err.Error(), "SMBus read word data not supported") +func TestWriteByte(t *testing.T) { + var tests = map[string]struct { + funcs uint64 + syscallImpl func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) + wantErr string + }{ + "write_byte_ok": { + funcs: I2C_FUNC_SMBUS_WRITE_BYTE, + }, + "error_syscall": { + funcs: I2C_FUNC_SMBUS_WRITE_BYTE, + syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 }, + wantErr: "Failed with syscall.Errno operation not permitted", + }, + "error_not_supported": { + wantErr: "SMBus write byte not supported", + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // arrange + d, msc := initTestI2cDeviceWithMockedSys() + defer cleanTestI2cDevice() + msc.Impl = tc.syscallImpl + d.funcs = tc.funcs + const val = byte(0x44) + // act + err := d.WriteByte(val) + // assert + if tc.wantErr != "" { + gobottest.Refute(t, err, nil) + gobottest.Assert(t, err.Error(), tc.wantErr) + } else { + gobottest.Assert(t, err, nil) + gobottest.Assert(t, msc.lastFile, d.file) + gobottest.Assert(t, msc.lastSignal, uintptr(I2C_SMBUS)) + gobottest.Assert(t, msc.smbus.readWrite, byte(I2C_SMBUS_WRITE)) + gobottest.Assert(t, msc.smbus.command, val) // in byte write, the register/command is used for the value + gobottest.Assert(t, msc.smbus.size, uint32(I2C_SMBUS_BYTE)) + } + }) + } } -func TestNewI2cDeviceWriteWordData(t *testing.T) { - fs := NewMockFilesystem([]string{ - "/dev/i2c-1", - }) - SetFilesystem(fs) - - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - i.funcs = I2C_FUNC_SMBUS_WRITE_WORD_DATA - - e := i.WriteWordData(0x01, 0x0102) - gobottest.Assert(t, e, nil) +func TestWriteByteData(t *testing.T) { + var tests = map[string]struct { + funcs uint64 + syscallImpl func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) + wantErr string + }{ + "write_byte_data_ok": { + funcs: I2C_FUNC_SMBUS_WRITE_BYTE_DATA, + }, + "error_syscall": { + funcs: I2C_FUNC_SMBUS_WRITE_BYTE_DATA, + syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 }, + wantErr: "Failed with syscall.Errno operation not permitted", + }, + "error_not_supported": { + wantErr: "SMBus write byte data not supported", + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // arrange + d, msc := initTestI2cDeviceWithMockedSys() + defer cleanTestI2cDevice() + msc.Impl = tc.syscallImpl + d.funcs = tc.funcs + const ( + reg = byte(0x04) + val = byte(0x55) + ) + // act + err := d.WriteByteData(reg, val) + // assert + if tc.wantErr != "" { + gobottest.Refute(t, err, nil) + gobottest.Assert(t, err.Error(), tc.wantErr) + } else { + gobottest.Assert(t, err, nil) + gobottest.Assert(t, msc.lastFile, d.file) + gobottest.Assert(t, msc.lastSignal, uintptr(I2C_SMBUS)) + gobottest.Assert(t, msc.smbus.readWrite, byte(I2C_SMBUS_WRITE)) + gobottest.Assert(t, msc.smbus.command, reg) + gobottest.Assert(t, msc.smbus.size, uint32(I2C_SMBUS_BYTE_DATA)) + gobottest.Assert(t, len(msc.dataSlice), 1) + gobottest.Assert(t, msc.dataSlice[0], val) + } + }) + } } -func TestNewI2cDeviceWriteWordDataNotSupported(t *testing.T) { - SetSyscall(&MockSyscall{}) - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - - err = i.WriteWordData(0x01, 0x01) - gobottest.Assert(t, err.Error(), "SMBus write word data not supported") +func TestWriteWordData(t *testing.T) { + var tests = map[string]struct { + funcs uint64 + syscallImpl func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) + wantErr string + }{ + "write_word_data_ok": { + funcs: I2C_FUNC_SMBUS_WRITE_WORD_DATA, + }, + "error_syscall": { + funcs: I2C_FUNC_SMBUS_WRITE_WORD_DATA, + syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 }, + wantErr: "Failed with syscall.Errno operation not permitted", + }, + "error_not_supported": { + wantErr: "SMBus write word data not supported", + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // arrange + d, msc := initTestI2cDeviceWithMockedSys() + defer cleanTestI2cDevice() + msc.Impl = tc.syscallImpl + d.funcs = tc.funcs + const ( + reg = byte(0x05) + val = uint16(54321) + wantLSByte = byte(0x31) + wantMSByte = byte(0xD4) + ) + // act + err := d.WriteWordData(reg, val) + // assert + if tc.wantErr != "" { + gobottest.Refute(t, err, nil) + gobottest.Assert(t, err.Error(), tc.wantErr) + } else { + gobottest.Assert(t, err, nil) + gobottest.Assert(t, msc.lastFile, d.file) + gobottest.Assert(t, msc.lastSignal, uintptr(I2C_SMBUS)) + gobottest.Assert(t, msc.smbus.readWrite, byte(I2C_SMBUS_WRITE)) + gobottest.Assert(t, msc.smbus.command, reg) + gobottest.Assert(t, msc.smbus.size, uint32(I2C_SMBUS_WORD_DATA)) + gobottest.Assert(t, len(msc.dataSlice), 2) + // all common drivers write LSByte first + gobottest.Assert(t, msc.dataSlice[0], wantLSByte) + gobottest.Assert(t, msc.dataSlice[1], wantMSByte) + } + }) + } } -func TestNewI2cDeviceWriteBlockData(t *testing.T) { - fs := NewMockFilesystem([]string{ - "/dev/i2c-1", - }) - SetFilesystem(fs) - - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - - e := i.WriteBlockData(0x01, []byte{0x01, 0x02, 0x03}) - gobottest.Assert(t, e, nil) +func TestWriteBlockData(t *testing.T) { + var tests = map[string]struct { + funcs uint64 + syscallImpl func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) + wantErr string + }{ + "write_word_data_ok": { + funcs: I2C_FUNC_SMBUS_WRITE_BLOCK_DATA, + }, + "error_syscall": { + funcs: I2C_FUNC_SMBUS_WRITE_BLOCK_DATA, + syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 }, + wantErr: "Failed with syscall.Errno operation not permitted", + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // arrange + d, msc := initTestI2cDeviceWithMockedSys() + defer cleanTestI2cDevice() + msc.Impl = tc.syscallImpl + d.funcs = tc.funcs + const ( + reg = byte(0x06) + byte0 = byte(0x66) + byte1 = byte(0x77) + byte2 = byte(0x88) + ) + data := []byte{byte0, byte1, byte2} + wantSize := uint32(len(data) + 1) // register is also part of send data + // act + err := d.WriteBlockData(reg, data) + // assert + if tc.wantErr != "" { + gobottest.Refute(t, err, nil) + gobottest.Assert(t, err.Error(), tc.wantErr) + } else { + gobottest.Assert(t, err, nil) + gobottest.Assert(t, msc.lastFile, d.file) + gobottest.Assert(t, msc.lastSignal, uintptr(I2C_SMBUS)) + gobottest.Assert(t, msc.smbus.readWrite, byte(I2C_SMBUS_WRITE)) + gobottest.Assert(t, msc.smbus.command, reg) + gobottest.Assert(t, msc.smbus.size, wantSize) + gobottest.Assert(t, msc.dataSlice, data) + } + }) + } } -func TestNewI2cDeviceWriteBlockDataTooMuch(t *testing.T) { - fs := NewMockFilesystem([]string{ - "/dev/i2c-1", - }) - SetFilesystem(fs) - - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - - var data []byte - data = make([]byte, 33) - e := i.WriteBlockData(0x01, data) - gobottest.Assert(t, e, errors.New("Writing blocks larger than 32 bytes (33) not supported")) -} - -func TestNewI2cDeviceWrite(t *testing.T) { - SetSyscall(&MockSyscall{}) - i, err := NewI2cDevice("/dev/i2c-1") - var _ i2c.I2cDevice = i - - gobottest.Assert(t, err, nil) - - i.SetAddress(0xff) - buf := []byte{0x01, 0x02, 0x03} - - n, err := i.Write(buf) - - gobottest.Assert(t, n, len(buf)) - gobottest.Assert(t, err, nil) +func TestWriteBlockDataTooMuch(t *testing.T) { + // arrange + SetFilesystem(NewMockFilesystem([]string{dev})) + defer SetFilesystem(&NativeFilesystem{}) + d, _ := NewI2cDevice(dev) + // act + err := d.WriteBlockData(0x01, make([]byte, 33)) + // assert + gobottest.Assert(t, err, errors.New("Writing blocks larger than 32 bytes (33) not supported")) } diff --git a/sysfs/pwm_pin_test.go b/sysfs/pwm_pin_test.go index 1ffeef71..9ba5c17f 100644 --- a/sysfs/pwm_pin_test.go +++ b/sysfs/pwm_pin_test.go @@ -21,6 +21,7 @@ func TestPwmPin(t *testing.T) { }) SetFilesystem(fs) + defer SetFilesystem(&NativeFilesystem{}) pin := NewPWMPin(10) gobottest.Assert(t, pin.pin, "10") @@ -73,6 +74,7 @@ func TestPwmPinAlreadyExported(t *testing.T) { }) SetFilesystem(fs) + defer SetFilesystem(&NativeFilesystem{}) pin := NewPWMPin(10) pin.write = func(string, []byte) (int, error) { @@ -93,6 +95,7 @@ func TestPwmPinExportError(t *testing.T) { }) SetFilesystem(fs) + defer SetFilesystem(&NativeFilesystem{}) pin := NewPWMPin(10) pin.write = func(string, []byte) (int, error) { @@ -113,6 +116,7 @@ func TestPwmPinUnxportError(t *testing.T) { }) SetFilesystem(fs) + defer SetFilesystem(&NativeFilesystem{}) pin := NewPWMPin(10) pin.write = func(string, []byte) (int, error) { @@ -132,6 +136,7 @@ func TestPwmPinPeriodError(t *testing.T) { }) SetFilesystem(fs) + defer SetFilesystem(&NativeFilesystem{}) pin := NewPWMPin(10) pin.read = func(string) ([]byte, error) { @@ -152,6 +157,7 @@ func TestPwmPinPolarityError(t *testing.T) { }) SetFilesystem(fs) + defer SetFilesystem(&NativeFilesystem{}) pin := NewPWMPin(10) pin.read = func(string) ([]byte, error) { @@ -172,6 +178,7 @@ func TestPwmPinDutyCycleError(t *testing.T) { }) SetFilesystem(fs) + defer SetFilesystem(&NativeFilesystem{}) pin := NewPWMPin(10) pin.read = func(string) ([]byte, error) { diff --git a/sysfs/syscall.go b/sysfs/syscall.go index af366892..08f6877c 100644 --- a/sysfs/syscall.go +++ b/sysfs/syscall.go @@ -2,19 +2,27 @@ package sysfs import ( "syscall" + "unsafe" ) // SystemCaller represents a Syscall +// Prevent unsafe call, since go 1.15, see "Pattern 4" in: https://go101.org/article/unsafe.html type SystemCaller interface { - Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) + Syscall(trap uintptr, f File, signal uintptr, payload unsafe.Pointer) (r1, r2 uintptr, err syscall.Errno) } // NativeSyscall represents the native Syscall type NativeSyscall struct{} -// MockSyscall represents the mock Syscall +// MockSyscall represents the mock Syscall used for unit tests type MockSyscall struct { - Impl func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) + lastTrap uintptr + lastFile File + lastSignal uintptr + devAddress uintptr + smbus *i2cSmbusIoctlData + dataSlice []byte + Impl func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) } var sys SystemCaller = &NativeSyscall{} @@ -25,19 +33,53 @@ func SetSyscall(s SystemCaller) { } // Syscall calls either the NativeSyscall or user defined Syscall -func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { - return sys.Syscall(trap, a1, a2, a3) +func Syscall(trap uintptr, f File, a2 uintptr, payload unsafe.Pointer) (r1, r2 uintptr, err syscall.Errno) { + return sys.Syscall(trap, f, a2, payload) } -// Syscall calls syscall.Syscall -func (sys *NativeSyscall) Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { - return syscall.Syscall(trap, a1, a2, a3) +// Syscall calls the native syscall.Syscall, implements the SystemCaller interface +func (sys *NativeSyscall) Syscall(trap uintptr, f File, signal uintptr, payload unsafe.Pointer) (r1, r2 uintptr, err syscall.Errno) { + return syscall.Syscall(trap, f.Fd(), signal, uintptr(payload)) } -// Syscall implements the SystemCaller interface -func (sys *MockSyscall) Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { +// Syscall calls the user defined implementation, used for tests, implements the SystemCaller interface +func (sys *MockSyscall) Syscall(trap uintptr, f File, signal uintptr, payload unsafe.Pointer) (r1, r2 uintptr, err syscall.Errno) { + sys.lastTrap = trap // points to the used syscall (e.g. "SYS_IOCTL") + sys.lastFile = f // a character device file (e.g. file to path "/dev/i2c-1") + sys.lastSignal = signal // points to used function type (e.g. I2C_SMBUS, I2C_RDWR) + + if signal == I2C_SLAVE { + // in this case the uintptr corresponds the address + sys.devAddress = uintptr(payload) + } + + if signal == I2C_SMBUS { + // set the I2C smbus data object reference to payload and fill with some data + sys.smbus = (*i2cSmbusIoctlData)(payload) + + // get the data object payload as byte slice + if sys.smbus.readWrite == I2C_SMBUS_WRITE { + if sys.smbus.data != nil { + sys.dataSlice = unsafe.Slice((*byte)(unsafe.Pointer(sys.smbus.data)), sys.smbus.size-1) + } + } + + // fill data object with data from given slice to simulate reading + if sys.smbus.readWrite == I2C_SMBUS_READ { + if sys.dataSlice != nil { + dataSize := sys.smbus.size - 1 + if sys.smbus.size == I2C_SMBUS_BYTE { + dataSize = 1 + } + slc := unsafe.Slice((*byte)(unsafe.Pointer(sys.smbus.data)), dataSize) + copy(slc, sys.dataSlice) + } + } + } + + // call mock implementation if sys.Impl != nil { - return sys.Impl(trap, a1, a2, a3) + return sys.Impl(trap, f.Fd(), signal, uintptr(unsafe.Pointer(payload))) } return 0, 0, 0 }