as7262: add internal context.context (#399)

Add internal context.context
Fixes some fragile time based tests.
Halt() is now synchronous, will only return if the context has been cancelled.
Remove go routine leak in New().
Fix spelling.
pull/1/head
NeuralSpaz 7 years ago committed by M-A
parent 53592c599b
commit b82bbe0a1c

@ -5,6 +5,7 @@
package as7262 package as7262
import ( import (
"context"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
@ -33,29 +34,35 @@ var DefaultOpts = Opts{
// New opens a handle to an AS7262 sensor. // New opens a handle to an AS7262 sensor.
func New(bus i2c.Bus, opts *Opts) (*Dev, error) { func New(bus i2c.Bus, opts *Opts) (*Dev, error) {
// The nil or zero values for gain and interrupt are valid // The nil or zero values for gain and interrupt are valid
c := make(chan struct{})
close(c)
return &Dev{ return &Dev{
c: &i2c.Dev{Bus: bus, Addr: 0x49}, c: &i2c.Dev{Bus: bus, Addr: 0x49},
gain: opts.Gain, gain: opts.Gain,
interrupt: opts.InterruptPin, interrupt: opts.InterruptPin,
done: make(chan struct{}, 1), cancel: func() {},
timeout: 200 * time.Millisecond, done: c,
}, nil }, nil
} }
var sensorTimeout = 200 * time.Millisecond
// waitForSensor is overridden in tests.
var waitForSensor = time.After
// Dev is a handle to the as7262 sensor. // Dev is a handle to the as7262 sensor.
type Dev struct { type Dev struct {
c conn.Conn c conn.Conn
timeout time.Duration
interrupt gpio.PinIn interrupt gpio.PinIn
// Mutable // Mutable
mu sync.Mutex mu sync.Mutex
gain Gain gain Gain
done chan struct{}
// Guards canceled // cancelMu guards cancel and done.
canceledMu sync.Mutex cancelMu sync.Mutex
canceled bool cancel context.CancelFunc
done chan struct{}
} }
// Spectrum is the reading from the sensor including the actual sensor state for // Spectrum is the reading from the sensor including the actual sensor state for
@ -123,25 +130,30 @@ func (b Band) String() string {
func (d *Dev) Sense(ledDrive physic.ElectricCurrent, senseTime time.Duration) (Spectrum, error) { func (d *Dev) Sense(ledDrive physic.ElectricCurrent, senseTime time.Duration) (Spectrum, error) {
d.mu.Lock() d.mu.Lock()
defer d.mu.Unlock()
done := make(chan struct{}, 1)
d.canceledMu.Lock()
d.done = done
d.canceled = false
d.canceledMu.Unlock()
it, integration := calcSenseTime(senseTime) it, integration := calcSenseTime(senseTime)
if err := d.writeVirtualRegister(intergrationReg, it); err != nil { ctx, cancel := context.WithCancel(context.Background())
d.cancelMu.Lock()
d.cancel = cancel
d.done = make(chan struct{})
d.cancelMu.Unlock()
defer func() {
close(d.done)
d.cancel()
d.mu.Unlock()
}()
if err := d.writeVirtualRegister(ctx, integrationReg, it); err != nil {
return Spectrum{}, err return Spectrum{}, err
} }
led, drive := calcLed(ledDrive) led, drive := calcLed(ledDrive)
if err := d.writeVirtualRegister(ledControlReg, led); err != nil { if err := d.writeVirtualRegister(ctx, ledControlReg, led); err != nil {
return Spectrum{}, err return Spectrum{}, err
} }
if err := d.writeVirtualRegister(controlReg, uint8(allOneShot)|uint8(d.gain)); err != nil { if err := d.writeVirtualRegister(ctx, controlReg, uint8(allOneShot)|uint8(d.gain)); err != nil {
return Spectrum{}, err return Spectrum{}, err
} }
@ -149,40 +161,40 @@ func (d *Dev) Sense(ledDrive physic.ElectricCurrent, senseTime time.Duration) (S
isEdge := make(chan bool) isEdge := make(chan bool)
go func() { go func() {
// TODO(NeuralSpaz): Test on hardware. // TODO(NeuralSpaz): Test on hardware.
isEdge <- d.interrupt.WaitForEdge(integration*2 + d.timeout) isEdge <- d.interrupt.WaitForEdge(integration*2 + sensorTimeout)
}() }()
select { select {
case edge := <-isEdge: case edge := <-isEdge:
if !edge { if !edge {
return Spectrum{}, errPinTimeout return Spectrum{}, errPinTimeout
} }
case <-d.done: case <-ctx.Done():
return Spectrum{}, errHalted return Spectrum{}, errHalted
} }
} else { } else {
select { select {
// WaitForSensor is time.After(). // WaitForSensor is time.After().
case <-waitForSensor(integration * 2): case <-waitForSensor(integration * 2):
if err := d.pollDataReady(); err != nil { if err := d.pollDataReady(ctx); err != nil {
return Spectrum{}, err return Spectrum{}, err
} }
case <-d.done: case <-ctx.Done():
return Spectrum{}, errHalted return Spectrum{}, errHalted
} }
} }
if err := d.writeVirtualRegister(ledControlReg, 0x00); err != nil { if err := d.writeVirtualRegister(ctx, ledControlReg, 0x00); err != nil {
return Spectrum{}, err return Spectrum{}, err
} }
raw := make([]byte, 12) raw := make([]byte, 12)
if err := d.readVirtualRegister(rawBase, raw); err != nil { if err := d.readVirtualRegister(ctx, rawBase, raw); err != nil {
return Spectrum{}, err return Spectrum{}, err
} }
cal := make([]byte, 24) cal := make([]byte, 24)
if err := d.readVirtualRegister(calBase, cal); err != nil { if err := d.readVirtualRegister(ctx, calBase, cal); err != nil {
return Spectrum{}, err return Spectrum{}, err
} }
@ -201,7 +213,7 @@ func (d *Dev) Sense(ledDrive physic.ElectricCurrent, senseTime time.Duration) (S
rcal := float64(math.Float32frombits(binary.BigEndian.Uint32(cal[20:24]))) rcal := float64(math.Float32frombits(binary.BigEndian.Uint32(cal[20:24])))
traw := make([]byte, 1) traw := make([]byte, 1)
if err := d.readVirtualRegister(deviceTemperatureReg, traw); err != nil { if err := d.readVirtualRegister(ctx, deviceTemperatureReg, traw); err != nil {
return Spectrum{}, err return Spectrum{}, err
} }
temperature := physic.Temperature(int8(traw[0]))*physic.Kelvin + physic.ZeroCelsius temperature := physic.Temperature(int8(traw[0]))*physic.Kelvin + physic.ZeroCelsius
@ -221,17 +233,14 @@ func (d *Dev) Sense(ledDrive physic.ElectricCurrent, senseTime time.Duration) (S
}, nil }, nil
} }
var waitForSensor = time.After
// Halt stops any pending operations. Repeated calls to Halt do nothing. // Halt stops any pending operations. Repeated calls to Halt do nothing.
func (d *Dev) Halt() error { func (d *Dev) Halt() error {
d.canceledMu.Lock() d.cancelMu.Lock()
defer d.canceledMu.Unlock() defer d.cancelMu.Unlock()
d.cancel()
if !d.canceled { // A receive can always proceed on a closed channel we can use that
d.done <- struct{}{} // to signal that the running process has been canceled correctly.
d.canceled = true _, _ = <-d.done
}
return nil return nil
} }
@ -284,13 +293,7 @@ func (d *Dev) Gain(gain Gain) error {
d.mu.Lock() d.mu.Lock()
defer d.mu.Unlock() defer d.mu.Unlock()
done := make(chan struct{}, 1) if err := d.writeVirtualRegister(context.Background(), controlReg, uint8(gain)); err != nil {
d.canceledMu.Lock()
d.done = done
d.canceled = false
d.canceledMu.Unlock()
if err := d.writeVirtualRegister(controlReg, uint8(gain)); err != nil {
return err return err
} }
d.gain = gain d.gain = gain
@ -301,10 +304,10 @@ func (d *Dev) Gain(gain Gain) error {
// MSB of the register must be set when writing the register to the write // MSB of the register must be set when writing the register to the write
// register, also status register must be checked for pending writes or data may // register, also status register must be checked for pending writes or data may
// be discarded. // be discarded.
func (d *Dev) writeVirtualRegister(register, data byte) error { func (d *Dev) writeVirtualRegister(ctx context.Context, register, data byte) error {
// Check for pending writes. // Check for pending writes.
if err := d.pollStatus(writing); err != nil { if err := d.pollStatus(ctx, writing); err != nil {
return err return err
} }
@ -314,7 +317,7 @@ func (d *Dev) writeVirtualRegister(register, data byte) error {
} }
// Check for pending writes again. // Check for pending writes again.
if err := d.pollStatus(writing); err != nil { if err := d.pollStatus(ctx, writing); err != nil {
return err return err
} }
@ -324,18 +327,17 @@ func (d *Dev) writeVirtualRegister(register, data byte) error {
} }
return nil return nil
} }
// AS7262 protocol uses virtual registers. To read a virtual register the // AS7262 protocol uses virtual registers. To read a virtual register the
// pointer to the virtual register must be written to the write register. Status // pointer to the virtual register must be written to the write register. Status
// register must be checked for any pending reads or data may be invalid, then // register must be checked for any pending reads or data may be invalid, then
// data maybe read from the read register. // data maybe read from the read register.
func (d *Dev) readVirtualRegister(register byte, data []byte) error { func (d *Dev) readVirtualRegister(ctx context.Context, register byte, data []byte) error {
rx := make([]byte, 1) rx := make([]byte, 1)
for i := 0; i < len(data); i++ { for i := 0; i < len(data); i++ {
// Check for pending reads. // Check for pending reads.
if err := d.pollStatus(clearBuffer); err != nil { if err := d.pollStatus(ctx, clearBuffer); err != nil {
return err return err
} }
@ -345,7 +347,7 @@ func (d *Dev) readVirtualRegister(register byte, data []byte) error {
} }
// Check if read buffer is ready. // Check if read buffer is ready.
if err := d.pollStatus(reading); err != nil { if err := d.pollStatus(ctx, reading); err != nil {
return err return err
} }
@ -360,12 +362,12 @@ func (d *Dev) readVirtualRegister(register byte, data []byte) error {
} }
// Polls the data ready bit in the control register(virtual) // Polls the data ready bit in the control register(virtual)
func (d *Dev) pollDataReady() error { func (d *Dev) pollDataReady(ctx context.Context) error {
timeout := time.NewTimer(d.timeout) timeout := time.NewTimer(sensorTimeout)
defer timeout.Stop() defer timeout.Stop()
for { for {
if err := d.pollStatus(clearBuffer); err != nil { if err := d.pollStatus(ctx, clearBuffer); err != nil {
return err return err
} }
@ -375,7 +377,7 @@ func (d *Dev) pollDataReady() error {
} }
// Check if read buffer is ready. // Check if read buffer is ready.
if err := d.pollStatus(reading); err != nil { if err := d.pollStatus(ctx, reading); err != nil {
return err return err
} }
@ -394,11 +396,10 @@ func (d *Dev) pollDataReady() error {
case <-timeout.C: case <-timeout.C:
// Return error if it takes too long. // Return error if it takes too long.
return errStatusDeadline return errStatusDeadline
case <-d.done: case <-ctx.Done():
return errHalted return errHalted
} }
} }
} }
type direction byte type direction byte
@ -416,19 +417,18 @@ const (
// provides a way to repeatedly check if there are any pending reads or writes // provides a way to repeatedly check if there are any pending reads or writes
// in the relevent buffer before a transaction while with a timeout. // in the relevent buffer before a transaction while with a timeout.
// Direction is used to set which buffer is being polled to be ready. // Direction is used to set which buffer is being polled to be ready.
func (d *Dev) pollStatus(dir direction) error { func (d *Dev) pollStatus(ctx context.Context, dir direction) error {
timeout := time.NewTimer(d.timeout) timeout := time.NewTimer(sensorTimeout)
defer timeout.Stop() defer timeout.Stop()
// Check if already canceled first
select { select {
case <-d.done: case <-ctx.Done():
return errHalted return errHalted
default: default:
// Proceed.
} }
status := make([]byte, 1) status := make([]byte, 1)
for { for {
// Read status register. // Read status register.
err := d.c.Tx([]byte{statusReg}, status) err := d.c.Tx([]byte{statusReg}, status)
if err != nil { if err != nil {
@ -462,13 +462,14 @@ func (d *Dev) pollStatus(dir direction) error {
return nil return nil
} }
} }
select { select {
case <-time.After(5 * time.Millisecond): case <-time.After(5 * time.Millisecond):
// Polling interval. // Polling interval.
case <-timeout.C: case <-timeout.C:
// Return error if it takes too long. // Return error if it takes too long.
return errStatusDeadline return errStatusDeadline
case <-d.done: case <-ctx.Done():
return errHalted return errHalted
} }
} }
@ -551,7 +552,7 @@ const (
hardwareVersion = 0x00 hardwareVersion = 0x00
firmwareVersion = 0x02 firmwareVersion = 0x02
controlReg = 0x04 controlReg = 0x04
intergrationReg = 0x05 integrationReg = 0x05
deviceTemperatureReg = 0x06 deviceTemperatureReg = 0x06
ledControlReg = 0x07 ledControlReg = 0x07
// RawBase used as base for reading uint16 values, data must be sequentially. // RawBase used as base for reading uint16 values, data must be sequentially.

@ -5,6 +5,7 @@
package as7262 package as7262
import ( import (
"context"
"reflect" "reflect"
"testing" "testing"
"time" "time"
@ -16,18 +17,16 @@ import (
"periph.io/x/periph/conn/physic" "periph.io/x/periph/conn/physic"
) )
var defaultSensorTimeOut = time.Millisecond * 200
func TestDev_Sense(t *testing.T) { func TestDev_Sense(t *testing.T) {
type timefunc func(*Dev) func(time.Duration) <-chan time.Time type timefunc func(*Dev) func(time.Duration) <-chan time.Time
defer func() {
waitForSensor = time.After
}()
haltit := func(dev *Dev) func(time.Duration) <-chan time.Time { haltit := func(dev *Dev) func(time.Duration) <-chan time.Time {
return func(d time.Duration) <-chan time.Time { return func(d time.Duration) <-chan time.Time {
t := make(chan time.Time, 1) t := make(chan time.Time, 1)
dev.Halt() go dev.Halt()
return t return t
} }
} }
@ -35,7 +34,7 @@ func TestDev_Sense(t *testing.T) {
return func(d time.Duration) <-chan time.Time { return func(d time.Duration) <-chan time.Time {
t := make(chan time.Time, 1) t := make(chan time.Time, 1)
t <- time.Now() t <- time.Now()
dev.timeout = time.Millisecond * 1 sensorTimeout = time.Millisecond * 1
return t return t
} }
} }
@ -89,17 +88,6 @@ func TestDev_Sense(t *testing.T) {
want: Spectrum{}, want: Spectrum{},
tx: sensorTestCaseInteruptValidRead, tx: sensorTestCaseInteruptValidRead,
}, },
{
name: "haltBeforeforEdge",
opts: Opts{
InterruptPin: intPin,
},
waiter: dontwait,
sendEdge: false,
wantErr: errHalted,
want: Spectrum{},
tx: sensorTestCaseInteruptValidRead,
},
{ {
name: "ioErrorWritingIntergrationReg", name: "ioErrorWritingIntergrationReg",
opts: Opts{ opts: Opts{
@ -169,39 +157,43 @@ func TestDev_Sense(t *testing.T) {
}, },
} }
for _, tt := range tests { for _, tt := range tests {
bus := &i2ctest.Playback{ t.Run(tt.name, func(t *testing.T) {
Ops: tt.tx,
DontPanic: true, defer func() {
} // cleanup
d, _ := New(bus, &tt.opts) waitForSensor = time.After
sensorTimeout = defaultSensorTimeOut
}()
waitForSensor = tt.waiter(d) bus := &i2ctest.Playback{
Ops: tt.tx,
DontPanic: true,
}
if d.interrupt != nil && tt.sendEdge { d, _ := New(bus, &tt.opts)
intPin.EdgesChan <- gpio.High
}
if tt.name == "haltBeforeforEdge" {
// Time must be less than senseTime.
time.AfterFunc(time.Millisecond, func() {
d.Halt()
})
}
got, err := d.Sense(physic.MilliAmpere*100, time.Millisecond*3) waitForSensor = tt.waiter(d)
if _, ok := tt.wantErr.(*IOError); ok { if d.interrupt != nil && tt.sendEdge {
if _, ok := err.(*IOError); !ok { intPin.EdgesChan <- gpio.High
t.Errorf("expected IOError but %T", err)
} }
if err.(*IOError).Op != tt.wantErr.(*IOError).Op {
t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, err.(*IOError).Op) got, err := d.Sense(physic.MilliAmpere*100, time.Millisecond*3)
if _, ok := tt.wantErr.(*IOError); ok {
if _, ok := err.(*IOError); !ok {
t.Errorf("expected IOError but %T", err)
}
if err.(*IOError).Op != tt.wantErr.(*IOError).Op {
t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, err.(*IOError).Op)
}
} else if err != tt.wantErr {
t.Errorf("expected error: %v but got: %v", tt.wantErr, got)
} }
} else if err != tt.wantErr { if !reflect.DeepEqual(got, tt.want) {
t.Errorf("expected error: %v but got: %v", tt.wantErr, got) t.Errorf("Dev.Sense() = %v, want %v", got, tt.want)
} }
if !reflect.DeepEqual(got, tt.want) { })
t.Errorf("Dev.Sense() = %v, want %v", got, tt.want)
}
} }
} }
@ -272,7 +264,7 @@ func TestDev_pollStatus(t *testing.T) {
{Addr: 0x49, W: []byte{statusReg}, R: []byte{}}, {Addr: 0x49, W: []byte{statusReg}, R: []byte{}},
}, },
dir: reading, dir: reading,
timeout: time.Millisecond * 1, timeout: time.Second,
wantErr: &IOError{"reading status register", nil}, wantErr: &IOError{"reading status register", nil},
}, },
{ {
@ -290,8 +282,8 @@ func TestDev_pollStatus(t *testing.T) {
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}},
}, },
dir: reading, dir: reading,
timeout: time.Millisecond * 1000, timeout: time.Second,
halt: time.Nanosecond, halt: 1,
wantErr: errHalted, wantErr: errHalted,
}, },
{ {
@ -300,7 +292,7 @@ func TestDev_pollStatus(t *testing.T) {
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}},
}, },
dir: reading, dir: reading,
timeout: time.Millisecond * 1000, timeout: time.Second,
wantErr: nil, wantErr: nil,
}, },
{ {
@ -309,7 +301,7 @@ func TestDev_pollStatus(t *testing.T) {
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}},
}, },
dir: writing, dir: writing,
timeout: time.Millisecond * 1000, timeout: time.Second,
wantErr: nil, wantErr: nil,
}, },
{ {
@ -318,7 +310,7 @@ func TestDev_pollStatus(t *testing.T) {
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}},
}, },
dir: clearBuffer, dir: clearBuffer,
timeout: time.Millisecond * 1000, timeout: time.Second,
wantErr: nil, wantErr: nil,
}, },
{ {
@ -329,7 +321,7 @@ func TestDev_pollStatus(t *testing.T) {
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}},
}, },
dir: clearBuffer, dir: clearBuffer,
timeout: time.Millisecond * 1000, timeout: time.Second,
wantErr: nil, wantErr: nil,
}, },
{ {
@ -339,68 +331,53 @@ func TestDev_pollStatus(t *testing.T) {
{Addr: 0x49, W: []byte{readReg}, R: []byte{}}, {Addr: 0x49, W: []byte{readReg}, R: []byte{}},
}, },
dir: clearBuffer, dir: clearBuffer,
timeout: time.Millisecond * 1000, timeout: time.Second,
wantErr: &IOError{"clearing buffer", nil}, wantErr: &IOError{"clearing buffer", nil},
}, },
{
name: "retry1",
tx: []i2ctest.IO{
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}},
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}},
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}},
},
dir: reading,
timeout: time.Millisecond * 100,
wantErr: nil,
},
{
name: "retrywithHalt",
tx: []i2ctest.IO{
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}},
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}},
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}},
},
dir: reading,
timeout: time.Millisecond * 100,
halt: time.Millisecond * 3,
wantErr: errHalted,
},
} }
for _, tt := range tests { for _, tt := range tests {
bus := &i2ctest.Playback{ t.Run(tt.name, func(t *testing.T) {
Ops: tt.tx, bus := &i2ctest.Playback{
DontPanic: true, Ops: tt.tx,
} DontPanic: true,
}
d := &Dev{ ctx, cancel := context.WithCancel(context.Background())
c: &i2c.Dev{Bus: bus, Addr: 0x49},
done: make(chan struct{}, 1), sensorTimeout = tt.timeout
timeout: tt.timeout, defer func() {
} // cleanup
defer d.Halt() sensorTimeout = defaultSensorTimeOut
if tt.halt > time.Nanosecond { cancel()
go func() {
time.Sleep(tt.halt)
d.Halt()
}() }()
} else if tt.halt != 0 {
d.Halt()
d.Halt()
}
got := d.pollStatus(tt.dir) d := &Dev{
// t.Errorf("expected error: %v but got: %v", tt.wantErr, got) c: &i2c.Dev{Bus: bus, Addr: 0x49},
cancel: cancel,
done: make(chan struct{}),
}
if _, ok := tt.wantErr.(*IOError); ok { if tt.halt != 0 {
if _, ok := got.(*IOError); !ok { go func() {
t.Errorf("expected IOError but %T", got) if err := d.Halt(); err != nil {
t.Errorf("Halt() expected nil but got %v", err)
}
}()
} }
if got.(*IOError).Op != tt.wantErr.(*IOError).Op {
t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, got.(*IOError).Op) got := d.pollStatus(ctx, tt.dir)
if _, ok := tt.wantErr.(*IOError); ok {
if _, ok := got.(*IOError); !ok {
t.Errorf("expected IOError but %T", got)
}
if got.(*IOError).Op != tt.wantErr.(*IOError).Op {
t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, got.(*IOError).Op)
}
} else if got != tt.wantErr {
t.Errorf("expected error: %v but got: %v", tt.wantErr, got)
} }
} else if got != tt.wantErr { })
t.Errorf("expected error: %v but got: %v", tt.wantErr, got)
}
} }
} }
@ -463,37 +440,47 @@ func TestDev_writeVirtualRegister(t *testing.T) {
}, },
} }
for _, tt := range tests { for _, tt := range tests {
bus := &i2ctest.Playback{ t.Run(tt.name, func(t *testing.T) {
Ops: tt.tx, bus := &i2ctest.Playback{
DontPanic: true, Ops: tt.tx,
} DontPanic: true,
}
d := &Dev{ ctx, cancel := context.WithCancel(context.Background())
c: &i2c.Dev{Bus: bus, Addr: 0x49},
done: make(chan struct{}, 1), sensorTimeout = tt.timeout
timeout: tt.timeout, defer func() {
} // cleanup
defer d.Halt() sensorTimeout = defaultSensorTimeOut
if tt.halt > time.Nanosecond { cancel()
go func() {
time.Sleep(tt.halt)
d.Halt()
}() }()
} else if tt.halt != 0 {
d.Halt()
}
got := d.writeVirtualRegister(0x04, 0xFF) d := &Dev{
if _, ok := tt.wantErr.(*IOError); ok { c: &i2c.Dev{Bus: bus, Addr: 0x49},
if _, ok := got.(*IOError); !ok { cancel: cancel,
t.Errorf("expected IOError but %T", got) done: make(chan struct{}),
} }
if got.(*IOError).Op != tt.wantErr.(*IOError).Op {
t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, got.(*IOError).Op) if tt.halt != 0 {
go func() {
if err := d.Halt(); err != nil {
t.Errorf("Halt() expected nil but got %v", err)
}
}()
} }
} else if got != tt.wantErr {
t.Errorf("expected error: %v but got: %v", tt.wantErr, got) got := d.writeVirtualRegister(ctx, 0x04, 0xFF)
} if _, ok := tt.wantErr.(*IOError); ok {
if _, ok := got.(*IOError); !ok {
t.Errorf("expected IOError but %T", got)
}
if got.(*IOError).Op != tt.wantErr.(*IOError).Op {
t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, got.(*IOError).Op)
}
} else if got != tt.wantErr {
t.Errorf("expected error: %v but got: %v", tt.wantErr, got)
}
})
} }
} }
@ -512,10 +499,15 @@ func TestDev_readVirtualRegister(t *testing.T) {
wantErr: nil, wantErr: nil,
}, },
{ {
name: "errHalted", name: "errHalted",
tx: []i2ctest.IO{}, tx: []i2ctest.IO{
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}},
{Addr: 0x49, W: []byte{writeReg, 0x04}, R: []byte{}},
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}},
{Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}},
},
data: []byte{0x00}, data: []byte{0x00},
halt: time.Nanosecond, halt: 1,
timeout: time.Millisecond * 100, timeout: time.Millisecond * 100,
wantErr: errHalted, wantErr: errHalted,
}, },
@ -523,7 +515,6 @@ func TestDev_readVirtualRegister(t *testing.T) {
name: "errClearingBuffer", name: "errClearingBuffer",
tx: []i2ctest.IO{ tx: []i2ctest.IO{
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}},
// {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x02}},
}, },
data: []byte{0x00}, data: []byte{0x00},
timeout: time.Millisecond * 100, timeout: time.Millisecond * 100,
@ -591,36 +582,47 @@ func TestDev_readVirtualRegister(t *testing.T) {
}, },
} }
for _, tt := range tests { for _, tt := range tests {
bus := &i2ctest.Playback{ t.Run(tt.name, func(t *testing.T) {
Ops: tt.tx, bus := &i2ctest.Playback{
DontPanic: true, Ops: tt.tx,
} DontPanic: true,
d := &Dev{ }
c: &i2c.Dev{Bus: bus, Addr: 0x49},
done: make(chan struct{}, 1), ctx, cancel := context.WithCancel(context.Background())
timeout: tt.timeout,
} sensorTimeout = tt.timeout
// defer d.Halt() defer func() {
if tt.halt > time.Nanosecond { // cleanup
go func() { sensorTimeout = defaultSensorTimeOut
time.Sleep(tt.halt) cancel()
d.Halt()
}() }()
} else if tt.halt != 0 {
d.Halt()
}
got := d.readVirtualRegister(0x04, tt.data) d := &Dev{
if _, ok := tt.wantErr.(*IOError); ok { c: &i2c.Dev{Bus: bus, Addr: 0x49},
if _, ok := got.(*IOError); !ok { cancel: cancel,
t.Errorf("expected IOError but %T", got) done: make(chan struct{}),
} }
if got.(*IOError).Op != tt.wantErr.(*IOError).Op {
t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, got.(*IOError).Op) if tt.halt != 0 {
go func() {
if err := d.Halt(); err != nil {
t.Errorf("Halt() expected nil but got %v", err)
}
}()
} }
} else if got != tt.wantErr {
t.Errorf("expected error: %v but got: %v", tt.wantErr, got) got := d.readVirtualRegister(ctx, 0x04, tt.data)
} if _, ok := tt.wantErr.(*IOError); ok {
if _, ok := got.(*IOError); !ok {
t.Errorf("expected IOError but %T", got)
}
if got.(*IOError).Op != tt.wantErr.(*IOError).Op {
t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, got.(*IOError).Op)
}
} else if got != tt.wantErr {
t.Errorf("expected error: %v but got: %v", tt.wantErr, got)
}
})
} }
} }
@ -633,10 +635,15 @@ func TestDev_pollDataReady(t *testing.T) {
wantErr error wantErr error
}{ }{
{ {
name: "errHalted", name: "errHalted",
tx: []i2ctest.IO{}, tx: []i2ctest.IO{
halt: time.Nanosecond, {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}},
timeout: time.Millisecond * 100, {Addr: 0x49, W: []byte{writeReg, controlReg}, R: []byte{}},
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}},
{Addr: 0x49, W: []byte{readReg}, R: []byte{0x01}},
},
halt: 1,
timeout: time.Millisecond * 1000,
wantErr: errHalted, wantErr: errHalted,
}, },
{ {
@ -644,7 +651,7 @@ func TestDev_pollDataReady(t *testing.T) {
tx: []i2ctest.IO{ tx: []i2ctest.IO{
{Addr: 0x49, W: []byte{statusReg}, R: []byte{}}, {Addr: 0x49, W: []byte{statusReg}, R: []byte{}},
}, },
timeout: time.Millisecond * 100, timeout: time.Millisecond * 1000,
wantErr: &IOError{"reading status register", nil}, wantErr: &IOError{"reading status register", nil},
}, },
{ {
@ -653,7 +660,7 @@ func TestDev_pollDataReady(t *testing.T) {
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}},
{Addr: 0x49, W: []byte{writeReg}, R: []byte{}}, {Addr: 0x49, W: []byte{writeReg}, R: []byte{}},
}, },
timeout: time.Millisecond * 100, timeout: time.Millisecond * 1000,
wantErr: &IOError{"setting virtual register", nil}, wantErr: &IOError{"setting virtual register", nil},
}, },
{ {
@ -663,7 +670,7 @@ func TestDev_pollDataReady(t *testing.T) {
{Addr: 0x49, W: []byte{writeReg, controlReg}, R: []byte{}}, {Addr: 0x49, W: []byte{writeReg, controlReg}, R: []byte{}},
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}},
}, },
timeout: time.Millisecond * 100, timeout: time.Millisecond * 1000,
wantErr: &IOError{"reading status register", nil}, wantErr: &IOError{"reading status register", nil},
}, },
{ {
@ -674,7 +681,7 @@ func TestDev_pollDataReady(t *testing.T) {
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}},
{Addr: 0x49, W: []byte{}, R: []byte{}}, {Addr: 0x49, W: []byte{}, R: []byte{}},
}, },
timeout: time.Millisecond * 100, timeout: time.Millisecond * 1000,
wantErr: &IOError{"reading virtual register", nil}, wantErr: &IOError{"reading virtual register", nil},
}, },
{ {
@ -696,7 +703,7 @@ func TestDev_pollDataReady(t *testing.T) {
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}},
{Addr: 0x49, W: []byte{readReg}, R: []byte{0x03}}, {Addr: 0x49, W: []byte{readReg}, R: []byte{0x03}},
}, },
timeout: time.Millisecond * 100, timeout: time.Millisecond * 1000,
wantErr: nil, wantErr: nil,
}, },
{ {
@ -732,37 +739,47 @@ func TestDev_pollDataReady(t *testing.T) {
}, },
} }
for _, tt := range tests { for _, tt := range tests {
bus := &i2ctest.Playback{ t.Run(tt.name, func(t *testing.T) {
Ops: tt.tx, bus := &i2ctest.Playback{
DontPanic: true, Ops: tt.tx,
} DontPanic: true,
}
d := &Dev{ ctx, cancel := context.WithCancel(context.Background())
c: &i2c.Dev{Bus: bus, Addr: 0x49},
done: make(chan struct{}, 1), sensorTimeout = tt.timeout
timeout: tt.timeout, defer func() {
} // cleanup
defer d.Halt() sensorTimeout = defaultSensorTimeOut
if tt.halt > time.Nanosecond { cancel()
go func() {
time.Sleep(tt.halt)
d.Halt()
}() }()
} else if tt.halt != 0 {
d.Halt()
}
got := d.pollDataReady() d := &Dev{
if _, ok := tt.wantErr.(*IOError); ok { c: &i2c.Dev{Bus: bus, Addr: 0x49},
if _, ok := got.(*IOError); !ok { cancel: cancel,
t.Errorf("expected IOError but %T", got) done: make(chan struct{}),
} }
if got.(*IOError).Op != tt.wantErr.(*IOError).Op {
t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, got.(*IOError).Op) if tt.halt != 0 {
go func() {
if err := d.Halt(); err != nil {
t.Errorf("Halt() expected nil but got %v", err)
}
}()
} }
} else if got != tt.wantErr {
t.Errorf("expected error: %v but got: %v", tt.wantErr, got) got := d.pollDataReady(ctx)
} if _, ok := tt.wantErr.(*IOError); ok {
if _, ok := got.(*IOError); !ok {
t.Errorf("expected IOError but %T", got)
}
if got.(*IOError).Op != tt.wantErr.(*IOError).Op {
t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, got.(*IOError).Op)
}
} else if got != tt.wantErr {
t.Errorf("expected error: %v but got: %v", tt.wantErr, got)
}
})
} }
} }
@ -796,6 +813,12 @@ func TestNew(t *testing.T) {
if tt.want2 != d.interrupt { if tt.want2 != d.interrupt {
t.Errorf("New() wanted %v but got %v", tt.want2, d.interrupt) t.Errorf("New() wanted %v but got %v", tt.want2, d.interrupt)
} }
// Halt with empty context.
err = d.Halt()
if err != nil {
t.Errorf("New Sensor halt wanted nil but got %v", err)
}
} }
} }
@ -812,24 +835,10 @@ func TestDev_Gain(t *testing.T) {
name: "errStatusIO", name: "errStatusIO",
tx: []i2ctest.IO{ tx: []i2ctest.IO{
{Addr: 0x49, W: []byte{statusReg}, R: []byte{}}, {Addr: 0x49, W: []byte{statusReg}, R: []byte{}},
// {Addr: 0x49, W: []byte{writeReg}, R: []byte{}},
}, },
timeout: time.Millisecond * 100, timeout: time.Millisecond * 100,
wantErr: &IOError{"reading status register", nil}, wantErr: &IOError{"reading status register", nil},
}, },
{
name: "errHalt",
tx: []i2ctest.IO{
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x03}},
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x03}},
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x03}},
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x03}},
{Addr: 0x49, W: []byte{statusReg}, R: []byte{0x03}},
},
halt: true,
timeout: time.Millisecond * 100,
wantErr: errHalted,
},
{ {
name: "errGainValue", name: "errGainValue",
gain: Gain(255), gain: Gain(255),
@ -854,21 +863,19 @@ func TestDev_Gain(t *testing.T) {
DontPanic: true, DontPanic: true,
} }
d, _ := New(bus, &DefaultOpts) d, _ := New(bus, &DefaultOpts)
go func() {
time.Sleep(time.Millisecond * 1) sensorTimeout = tt.timeout
d.Halt()
}()
got := d.Gain(tt.gain) got := d.Gain(tt.gain)
if _, ok := tt.wantErr.(*IOError); ok { if _, ok := tt.wantErr.(*IOError); ok {
if _, ok := got.(*IOError); !ok { if _, ok := got.(*IOError); !ok {
t.Errorf("expected IOError but %T", got) t.Errorf("%s: expected IOError but %T", tt.name, got)
} }
if got.(*IOError).Op != tt.wantErr.(*IOError).Op { if got.(*IOError).Op != tt.wantErr.(*IOError).Op {
t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, got.(*IOError).Op) t.Errorf("%s: expected %s, but got %s", tt.name, tt.wantErr.(*IOError).Op, got.(*IOError).Op)
} }
} else if got != tt.wantErr { } else if got != tt.wantErr {
t.Errorf("expected error: %v but got: %v", tt.wantErr, got) t.Errorf("%s: expected error: %v but got: %v", tt.name, tt.wantErr, got)
} }
} }
} }
@ -940,20 +947,3 @@ func TestIOError_Error(t *testing.T) {
} }
} }
} }
func TestIntergration_AfterHalt(t *testing.T) {
bus := &i2ctest.Playback{
Ops: sensorTestCaseValidRead,
DontPanic: true,
}
d, _ := New(bus, &DefaultOpts)
d.Halt()
if _, err := d.Sense(physic.MilliAmpere*100, time.Millisecond*3); err == errHalted {
t.Errorf("got %v expected nil", errHalted)
}
d.Halt()
if err := d.Gain(G1x); err == errHalted {
t.Errorf("got %v expected nil", errHalted)
}
}

Loading…
Cancel
Save