Parallelize tests (#17)
* Store a reference to libusb implementation in the context, transfers and some more places. Remove the global libusb variable. * Parallelize tests. * Fix the link in README.
This commit is contained in:
@@ -13,7 +13,8 @@ Supported platforms include:
|
||||
- darwin
|
||||
- windows
|
||||
|
||||
This is the release 2.0 of the package [github.com/kylelemons/gousb]. Its API is not backwards-compatible with version 1.0.
|
||||
This is the release 2.0 of the package [github.com/kylelemons/gousb](https://github.com/kylelemons/gousb).
|
||||
Its API is not backwards-compatible with version 1.0.
|
||||
As of 2017-07-13 the 2.0 API is considered stable and 1.0 is deprecated.
|
||||
|
||||
[coverimg]: https://coveralls.io/repos/github/google/gousb/badge.svg
|
||||
|
@@ -120,12 +120,12 @@ func (c *Config) Interface(num, alt int) (*Interface, error) {
|
||||
}
|
||||
|
||||
// Claim the interface
|
||||
if err := libusb.claim(c.dev.handle, uint8(num)); err != nil {
|
||||
if err := c.dev.ctx.libusb.claim(c.dev.handle, uint8(num)); err != nil {
|
||||
return nil, fmt.Errorf("failed to claim interface %d on %s: %v", num, c, err)
|
||||
}
|
||||
|
||||
if err := libusb.setAlt(c.dev.handle, uint8(num), uint8(alt)); err != nil {
|
||||
libusb.release(c.dev.handle, uint8(num))
|
||||
if err := c.dev.ctx.libusb.setAlt(c.dev.handle, uint8(num), uint8(alt)); err != nil {
|
||||
c.dev.ctx.libusb.release(c.dev.handle, uint8(num))
|
||||
return nil, fmt.Errorf("failed to set alternate config %d on interface %d of %s: %v", alt, num, c, err)
|
||||
}
|
||||
|
||||
|
17
device.go
17
device.go
@@ -80,6 +80,7 @@ func (d *DeviceDesc) cfgDesc(cfgNum int) (*ConfigDesc, error) {
|
||||
// A Device must be Close()d after use.
|
||||
type Device struct {
|
||||
handle *libusbDevHandle
|
||||
ctx *Context
|
||||
|
||||
// Embed the device information for easy access
|
||||
Desc *DeviceDesc
|
||||
@@ -109,7 +110,7 @@ func (d *Device) Reset() error {
|
||||
if d.claimed != nil {
|
||||
return fmt.Errorf("can't reset device %s while it has an active configuration %s", d, d.claimed)
|
||||
}
|
||||
return libusb.reset(d.handle)
|
||||
return d.ctx.libusb.reset(d.handle)
|
||||
}
|
||||
|
||||
// ActiveConfigNum returns the config id of the active configuration.
|
||||
@@ -119,7 +120,7 @@ func (d *Device) ActiveConfigNum() (int, error) {
|
||||
if d.handle == nil {
|
||||
return 0, fmt.Errorf("ActiveConfig() called on %s after Close", d)
|
||||
}
|
||||
ret, err := libusb.getConfig(d.handle)
|
||||
ret, err := d.ctx.libusb.getConfig(d.handle)
|
||||
return int(ret), err
|
||||
}
|
||||
|
||||
@@ -146,7 +147,7 @@ func (d *Device) Config(cfgNum int) (*Config, error) {
|
||||
|
||||
if d.autodetach {
|
||||
for _, iface := range cfg.Desc.Interfaces {
|
||||
if err := libusb.detachKernelDriver(d.handle, uint8(iface.Number)); err != nil {
|
||||
if err := d.ctx.libusb.detachKernelDriver(d.handle, uint8(iface.Number)); err != nil {
|
||||
return nil, fmt.Errorf("Can't detach kernel driver of the device %s and interface %d: %v", d, iface.Number, err)
|
||||
}
|
||||
}
|
||||
@@ -155,7 +156,7 @@ func (d *Device) Config(cfgNum int) (*Config, error) {
|
||||
if activeCfgNum, err := d.ActiveConfigNum(); err != nil {
|
||||
return nil, fmt.Errorf("failed to query active config of the device %s: %v", d, err)
|
||||
} else if cfgNum != activeCfgNum {
|
||||
if err := libusb.setConfig(d.handle, uint8(cfgNum)); err != nil {
|
||||
if err := d.ctx.libusb.setConfig(d.handle, uint8(cfgNum)); err != nil {
|
||||
return nil, fmt.Errorf("failed to set active config %d for the device %s: %v", cfgNum, d, err)
|
||||
}
|
||||
}
|
||||
@@ -194,7 +195,7 @@ func (d *Device) Control(rType, request uint8, val, idx uint16, data []byte) (in
|
||||
if d.handle == nil {
|
||||
return 0, fmt.Errorf("Control() called on %s after Close", d)
|
||||
}
|
||||
return libusb.control(d.handle, d.ControlTimeout, rType, request, val, idx, data)
|
||||
return d.ctx.libusb.control(d.handle, d.ControlTimeout, rType, request, val, idx, data)
|
||||
}
|
||||
|
||||
// Close closes the device.
|
||||
@@ -207,7 +208,7 @@ func (d *Device) Close() error {
|
||||
if d.claimed != nil {
|
||||
return fmt.Errorf("can't release the device %s, it has an open config %d", d, d.claimed.Desc.Number)
|
||||
}
|
||||
libusb.close(d.handle)
|
||||
d.ctx.libusb.close(d.handle)
|
||||
d.handle = nil
|
||||
return nil
|
||||
}
|
||||
@@ -224,7 +225,7 @@ func (d *Device) GetStringDescriptor(descIndex int) (string, error) {
|
||||
if descIndex == 0 {
|
||||
return "", nil
|
||||
}
|
||||
return libusb.getStringDesc(d.handle, descIndex)
|
||||
return d.ctx.libusb.getStringDesc(d.handle, descIndex)
|
||||
}
|
||||
|
||||
// Manufacturer returns the device's manufacturer name.
|
||||
@@ -283,5 +284,5 @@ func (d *Device) SetAutoDetach(autodetach bool) error {
|
||||
if autodetach {
|
||||
autodetachInt = 1
|
||||
}
|
||||
return libusb.setAutoDetach(d.handle, autodetachInt)
|
||||
return d.ctx.libusb.setAutoDetach(d.handle, autodetachInt)
|
||||
}
|
||||
|
@@ -21,10 +21,7 @@ import (
|
||||
)
|
||||
|
||||
func TestClaimAndRelease(t *testing.T) {
|
||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
||||
_, done := newFakeLibusb()
|
||||
defer done()
|
||||
|
||||
t.Parallel()
|
||||
const (
|
||||
devIdx = 1
|
||||
cfgNum = 1
|
||||
@@ -34,8 +31,14 @@ func TestClaimAndRelease(t *testing.T) {
|
||||
alt2Num = 0
|
||||
if2Num = 0
|
||||
)
|
||||
c := NewContext()
|
||||
defer c.Close()
|
||||
|
||||
c := newContextWithImpl(newFakeLibusb())
|
||||
defer func() {
|
||||
if err := c.Close(); err != nil {
|
||||
t.Errorf("Context.Close: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002)
|
||||
if dev == nil {
|
||||
t.Fatal("OpenDeviceWithVIDPID(0x8888, 0x0002): got nil device, need non-nil")
|
||||
@@ -177,10 +180,7 @@ func TestClaimAndRelease(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInterfaceDescriptionError(t *testing.T) {
|
||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
||||
_, done := newFakeLibusb()
|
||||
defer done()
|
||||
|
||||
t.Parallel()
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
cfg, intf, alt int
|
||||
@@ -189,10 +189,15 @@ func TestInterfaceDescriptionError(t *testing.T) {
|
||||
{"no interface", 1, 3, 1},
|
||||
{"no alt setting", 1, 1, 5},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Can't be parallelized, depends on the shared global state set before the loop.
|
||||
c := NewContext()
|
||||
defer c.Close()
|
||||
t.Parallel()
|
||||
c := newContextWithImpl(newFakeLibusb())
|
||||
defer func() {
|
||||
if err := c.Close(); err != nil {
|
||||
t.Errorf("Context.Close(): %v", err)
|
||||
}
|
||||
}()
|
||||
dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002)
|
||||
if dev == nil {
|
||||
t.Fatal("OpenDeviceWithVIDPID(0x8888, 0x0002): got nil device, need non-nil")
|
||||
@@ -220,12 +225,9 @@ func (*failDetachLib) detachKernelDriver(h *libusbDevHandle, i uint8) error {
|
||||
}
|
||||
|
||||
func TestAutoDetachFailure(t *testing.T) {
|
||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
||||
fake, done := newFakeLibusb()
|
||||
defer done()
|
||||
libusb = &failDetachLib{fake}
|
||||
|
||||
c := NewContext()
|
||||
t.Parallel()
|
||||
fake := newFakeLibusb()
|
||||
c := newContextWithImpl(&failDetachLib{fake})
|
||||
defer c.Close()
|
||||
dev, err := c.OpenDeviceWithVIDPID(0x8888, 0x0002)
|
||||
if dev == nil {
|
||||
|
@@ -77,6 +77,8 @@ type endpoint struct {
|
||||
Desc EndpointDesc
|
||||
|
||||
Timeout time.Duration
|
||||
|
||||
ctx *Context
|
||||
}
|
||||
|
||||
// String returns a human-readable description of the endpoint.
|
||||
@@ -89,7 +91,7 @@ func (e *endpoint) transfer(buf []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
t, err := newUSBTransfer(e.h, &e.Desc, len(buf), e.Timeout)
|
||||
t, err := newUSBTransfer(e.ctx, e.h, &e.Desc, len(buf), e.Timeout)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ package gousb
|
||||
func (e *endpoint) newStream(size, count int, submit bool) (*stream, error) {
|
||||
var ts []transferIntf
|
||||
for i := 0; i < count; i++ {
|
||||
t, err := newUSBTransfer(e.h, &e.Desc, size, e.Timeout)
|
||||
t, err := newUSBTransfer(e.ctx, e.h, &e.Desc, size, e.Timeout)
|
||||
if err != nil {
|
||||
for _, t := range ts {
|
||||
t.free()
|
||||
|
@@ -17,9 +17,14 @@ package gousb
|
||||
import "testing"
|
||||
|
||||
func TestEndpointReadStream(t *testing.T) {
|
||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
||||
lib, done := newFakeLibusb()
|
||||
defer done()
|
||||
t.Parallel()
|
||||
lib := newFakeLibusb()
|
||||
ctx := newContextWithImpl(lib)
|
||||
defer func() {
|
||||
if err := ctx.Close(); err != nil {
|
||||
t.Errorf("Context.Close: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
goodTransfers := 7
|
||||
go func() {
|
||||
@@ -41,7 +46,6 @@ func TestEndpointReadStream(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
ctx := NewContext()
|
||||
dev, err := ctx.OpenDeviceWithVIDPID(0x9999, 0x0001)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenDeviceWithVIDPID(9999, 0001): %v", err)
|
||||
|
@@ -20,9 +20,14 @@ import (
|
||||
)
|
||||
|
||||
func TestEndpoint(t *testing.T) {
|
||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
||||
lib, done := newFakeLibusb()
|
||||
defer done()
|
||||
t.Parallel()
|
||||
lib := newFakeLibusb()
|
||||
ctx := newContextWithImpl(lib)
|
||||
defer func() {
|
||||
if err := ctx.Close(); err != nil {
|
||||
t.Errorf("Context.Close(): %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
for _, epData := range []struct {
|
||||
ei EndpointDesc
|
||||
@@ -92,7 +97,7 @@ func TestEndpoint(t *testing.T) {
|
||||
wantErr: true,
|
||||
},
|
||||
} {
|
||||
ep := &endpoint{h: nil, InterfaceSetting: epData.intf, Desc: epData.ei}
|
||||
ep := &endpoint{h: nil, ctx: ctx, InterfaceSetting: epData.intf, Desc: epData.ei}
|
||||
if tc.wantSubmit {
|
||||
go func() {
|
||||
fakeT := lib.waitForSubmitted()
|
||||
@@ -163,12 +168,15 @@ func TestEndpointInfo(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEndpointInOut(t *testing.T) {
|
||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
||||
lib, done := newFakeLibusb()
|
||||
defer done()
|
||||
t.Parallel()
|
||||
lib := newFakeLibusb()
|
||||
ctx := newContextWithImpl(lib)
|
||||
defer func() {
|
||||
if err := ctx.Close(); err != nil {
|
||||
t.Errorf("Context.Close(): %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
ctx := NewContext()
|
||||
defer ctx.Close()
|
||||
d, err := ctx.OpenDeviceWithVIDPID(0x9999, 0x0001)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenDeviceWithVIDPID(0x9999, 0x0001): got error %v, want nil", err)
|
||||
@@ -243,12 +251,14 @@ func TestEndpointInOut(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSameEndpointNumberInOut(t *testing.T) {
|
||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
||||
_, done := newFakeLibusb()
|
||||
defer done()
|
||||
t.Parallel()
|
||||
ctx := newContextWithImpl(newFakeLibusb())
|
||||
defer func() {
|
||||
if err := ctx.Close(); err != nil {
|
||||
t.Errorf("Context.Close(): %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
ctx := NewContext()
|
||||
defer ctx.Close()
|
||||
d, err := ctx.OpenDeviceWithVIDPID(0x1111, 0x1111)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenDeviceWithVIDPID(0x1111, 0x1111): got error %v, want nil", err)
|
||||
|
@@ -64,9 +64,19 @@ func (f *fakeLibusb) getDevices(*libusbContext) ([]*libusbDevice, error) {
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
func (f *fakeLibusb) exit(*libusbContext) {}
|
||||
func (f *fakeLibusb) setDebug(*libusbContext, int) {}
|
||||
|
||||
func (f *fakeLibusb) exit(*libusbContext) error {
|
||||
close(f.submitted)
|
||||
if got := len(f.ts); got > 0 {
|
||||
for t := range f.ts {
|
||||
f.free(t)
|
||||
}
|
||||
return fmt.Errorf("fakeLibusb has %d remaining transfers that should have been freed", got)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeLibusb) setDebug(*libusbContext, int) {}
|
||||
func (f *fakeLibusb) dereference(d *libusbDevice) {}
|
||||
func (f *fakeLibusb) getDeviceDesc(d *libusbDevice) (*DeviceDesc, error) {
|
||||
if dev, ok := f.fakeDevices[d]; ok {
|
||||
@@ -191,8 +201,7 @@ func (f *fakeLibusb) empty() bool {
|
||||
return len(f.submitted) == 0
|
||||
}
|
||||
|
||||
func newFakeLibusb() (*fakeLibusb, func() error) {
|
||||
origLibusb := libusb
|
||||
func newFakeLibusb() *fakeLibusb {
|
||||
fl := &fakeLibusb{
|
||||
fakeDevices: make(map[*libusbDevice]*fakeDevice),
|
||||
ts: make(map[*libusbTransfer]*fakeTransfer),
|
||||
@@ -209,16 +218,5 @@ func newFakeLibusb() (*fakeLibusb, func() error) {
|
||||
*fd = d
|
||||
fl.fakeDevices[newDevicePointer()] = fd
|
||||
}
|
||||
libusb = fl
|
||||
return fl, func() error {
|
||||
defer func() { libusb = origLibusb }()
|
||||
close(fl.submitted)
|
||||
if got := len(fl.ts); got > 0 {
|
||||
for t := range fl.ts {
|
||||
fl.free(t)
|
||||
}
|
||||
return fmt.Errorf("fakeLibusb has %d remaining transfers that should have been freed", got)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fl
|
||||
}
|
||||
|
@@ -89,7 +89,7 @@ func (i *Interface) Close() {
|
||||
if i.config == nil {
|
||||
return
|
||||
}
|
||||
libusb.release(i.config.dev.handle, uint8(i.Setting.Number))
|
||||
i.config.dev.ctx.libusb.release(i.config.dev.handle, uint8(i.Setting.Number))
|
||||
i.config.mu.Lock()
|
||||
defer i.config.mu.Unlock()
|
||||
delete(i.config.claimed, i.Setting.Number)
|
||||
@@ -106,6 +106,7 @@ func (i *Interface) openEndpoint(epAddr EndpointAddress) (*endpoint, error) {
|
||||
InterfaceSetting: i.Setting,
|
||||
Desc: ep,
|
||||
h: i.config.dev.handle,
|
||||
ctx: i.config.dev.ctx,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@@ -135,7 +135,7 @@ type libusbIntf interface {
|
||||
init() (*libusbContext, error)
|
||||
handleEvents(*libusbContext, <-chan struct{})
|
||||
getDevices(*libusbContext) ([]*libusbDevice, error)
|
||||
exit(*libusbContext)
|
||||
exit(*libusbContext) error
|
||||
setDebug(*libusbContext, int)
|
||||
|
||||
// device
|
||||
@@ -213,8 +213,9 @@ func (libusbImpl) getDevices(ctx *libusbContext) ([]*libusbDevice, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (libusbImpl) exit(c *libusbContext) {
|
||||
func (libusbImpl) exit(c *libusbContext) error {
|
||||
C.libusb_exit((*C.libusb_context)(c))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (libusbImpl) setDebug(c *libusbContext, lvl int) {
|
||||
@@ -479,9 +480,6 @@ func (libusbImpl) setIsoPacketLengths(t *libusbTransfer, length uint32) {
|
||||
C.libusb_set_iso_packet_lengths((*C.struct_libusb_transfer)(t), C.uint(length))
|
||||
}
|
||||
|
||||
// libusb is an injection point for tests
|
||||
var libusb libusbIntf = libusbImpl{}
|
||||
|
||||
// xferDoneMap keeps a map of done callback channels for all allocated transfers.
|
||||
var xferDoneMap = struct {
|
||||
m map[*libusbTransfer]chan struct{}
|
||||
|
19
transfer.go
19
transfer.go
@@ -35,6 +35,8 @@ type usbTransfer struct {
|
||||
done chan struct{}
|
||||
// submitted is true if submit() was called on this transfer.
|
||||
submitted bool
|
||||
// ctx is the Context that created this transfer.
|
||||
ctx *Context
|
||||
}
|
||||
|
||||
// submits the transfer. After submit() the transfer is in flight and is owned by libusb.
|
||||
@@ -46,7 +48,7 @@ func (t *usbTransfer) submit() error {
|
||||
if t.submitted {
|
||||
return errors.New("transfer was already submitted and is not finished yet")
|
||||
}
|
||||
if err := libusb.submit(t.xfer); err != nil {
|
||||
if err := t.ctx.libusb.submit(t.xfer); err != nil {
|
||||
return err
|
||||
}
|
||||
t.submitted = true
|
||||
@@ -66,7 +68,7 @@ func (t *usbTransfer) wait() (n int, err error) {
|
||||
}
|
||||
<-t.done
|
||||
t.submitted = false
|
||||
n, status := libusb.data(t.xfer)
|
||||
n, status := t.ctx.libusb.data(t.xfer)
|
||||
if status != TransferCompleted {
|
||||
return n, status
|
||||
}
|
||||
@@ -81,7 +83,7 @@ func (t *usbTransfer) cancel() error {
|
||||
if !t.submitted {
|
||||
return nil
|
||||
}
|
||||
err := libusb.cancel(t.xfer)
|
||||
err := t.ctx.libusb.cancel(t.xfer)
|
||||
if err == ErrorNotFound {
|
||||
// transfer already completed
|
||||
return nil
|
||||
@@ -101,7 +103,7 @@ func (t *usbTransfer) free() error {
|
||||
if t.xfer == nil {
|
||||
return nil
|
||||
}
|
||||
libusb.free(t.xfer)
|
||||
t.ctx.libusb.free(t.xfer)
|
||||
t.xfer = nil
|
||||
t.buf = nil
|
||||
t.done = nil
|
||||
@@ -115,7 +117,7 @@ func (t *usbTransfer) data() []byte {
|
||||
|
||||
// newUSBTransfer allocates a new transfer structure and a new buffer for
|
||||
// communication with a given device/endpoint.
|
||||
func newUSBTransfer(dev *libusbDevHandle, ei *EndpointDesc, bufLen int, timeout time.Duration) (*usbTransfer, error) {
|
||||
func newUSBTransfer(ctx *Context, dev *libusbDevHandle, ei *EndpointDesc, bufLen int, timeout time.Duration) (*usbTransfer, error) {
|
||||
var isoPackets, isoPktSize int
|
||||
if ei.TransferType == TransferTypeIsochronous {
|
||||
isoPktSize = ei.MaxPacketSize
|
||||
@@ -127,19 +129,20 @@ func newUSBTransfer(dev *libusbDevHandle, ei *EndpointDesc, bufLen int, timeout
|
||||
}
|
||||
|
||||
done := make(chan struct{}, 1)
|
||||
xfer, err := libusb.alloc(dev, ei, timeout, isoPackets, bufLen, done)
|
||||
xfer, err := ctx.libusb.alloc(dev, ei, timeout, isoPackets, bufLen, done)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ei.TransferType == TransferTypeIsochronous {
|
||||
libusb.setIsoPacketLengths(xfer, uint32(isoPktSize))
|
||||
ctx.libusb.setIsoPacketLengths(xfer, uint32(isoPktSize))
|
||||
}
|
||||
|
||||
t := &usbTransfer{
|
||||
xfer: xfer,
|
||||
buf: libusb.buffer(xfer),
|
||||
buf: ctx.libusb.buffer(xfer),
|
||||
done: done,
|
||||
ctx: ctx,
|
||||
}
|
||||
runtime.SetFinalizer(t, func(t *usbTransfer) {
|
||||
t.cancel()
|
||||
|
@@ -20,9 +20,13 @@ import (
|
||||
)
|
||||
|
||||
func TestNewTransfer(t *testing.T) {
|
||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
||||
_, done := newFakeLibusb()
|
||||
defer done()
|
||||
t.Parallel()
|
||||
ctx := newContextWithImpl(newFakeLibusb())
|
||||
defer func() {
|
||||
if err := ctx.Close(); err != nil {
|
||||
t.Errorf("Context.Close(): %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
for _, tc := range []struct {
|
||||
desc string
|
||||
@@ -53,7 +57,7 @@ func TestNewTransfer(t *testing.T) {
|
||||
wantLength: 10000,
|
||||
},
|
||||
} {
|
||||
xfer, err := newUSBTransfer(nil, &EndpointDesc{
|
||||
xfer, err := newUSBTransfer(ctx, nil, &EndpointDesc{
|
||||
Number: 2,
|
||||
Direction: tc.dir,
|
||||
TransferType: tc.tt,
|
||||
@@ -71,14 +75,19 @@ func TestNewTransfer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTransferProtocol(t *testing.T) {
|
||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
||||
f, done := newFakeLibusb()
|
||||
defer done()
|
||||
t.Parallel()
|
||||
f := newFakeLibusb()
|
||||
ctx := newContextWithImpl(f)
|
||||
defer func() {
|
||||
if err := ctx.Close(); err != nil {
|
||||
t.Errorf("Context.Close(): %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
xfers := make([]*usbTransfer, 2)
|
||||
var err error
|
||||
for i := 0; i < 2; i++ {
|
||||
xfers[i], err = newUSBTransfer(nil, &EndpointDesc{
|
||||
xfers[i], err = newUSBTransfer(ctx, nil, &EndpointDesc{
|
||||
Number: 6,
|
||||
Direction: EndpointDirectionIn,
|
||||
TransferType: TransferTypeBulk,
|
||||
|
35
usb.go
35
usb.go
@@ -129,35 +129,41 @@ package gousb
|
||||
type Context struct {
|
||||
ctx *libusbContext
|
||||
done chan struct{}
|
||||
libusb libusbIntf
|
||||
}
|
||||
|
||||
// Debug changes the debug level. Level 0 means no debug, higher levels
|
||||
// will print out more debugging information.
|
||||
func (c *Context) Debug(level int) {
|
||||
libusb.setDebug(c.ctx, level)
|
||||
c.libusb.setDebug(c.ctx, level)
|
||||
}
|
||||
|
||||
// NewContext returns a new Context instance.
|
||||
func NewContext() *Context {
|
||||
c, err := libusb.init()
|
||||
func newContextWithImpl(impl libusbIntf) *Context {
|
||||
c, err := impl.init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ctx := &Context{
|
||||
ctx: c,
|
||||
done: make(chan struct{}),
|
||||
libusb: impl,
|
||||
}
|
||||
go libusb.handleEvents(ctx.ctx, ctx.done)
|
||||
go impl.handleEvents(ctx.ctx, ctx.done)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// NewContext returns a new Context instance.
|
||||
func NewContext() *Context {
|
||||
return newContextWithImpl(libusbImpl{})
|
||||
}
|
||||
|
||||
// OpenDevices calls opener with each enumerated device.
|
||||
// If the opener returns true, the device is opened and a Device is returned if the operation succeeds.
|
||||
// Every Device returned (whether an error is also returned or not) must be closed.
|
||||
// If there are any errors enumerating the devices,
|
||||
// the final one is returned along with any successfully opened devices.
|
||||
func (c *Context) OpenDevices(opener func(desc *DeviceDesc) bool) ([]*Device, error) {
|
||||
list, err := libusb.getDevices(c.ctx)
|
||||
list, err := c.libusb.getDevices(c.ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -165,23 +171,23 @@ func (c *Context) OpenDevices(opener func(desc *DeviceDesc) bool) ([]*Device, er
|
||||
var reterr error
|
||||
var ret []*Device
|
||||
for _, dev := range list {
|
||||
desc, err := libusb.getDeviceDesc(dev)
|
||||
desc, err := c.libusb.getDeviceDesc(dev)
|
||||
if err != nil {
|
||||
libusb.dereference(dev)
|
||||
c.libusb.dereference(dev)
|
||||
reterr = err
|
||||
continue
|
||||
}
|
||||
|
||||
if opener(desc) {
|
||||
handle, err := libusb.open(dev)
|
||||
handle, err := c.libusb.open(dev)
|
||||
if err != nil {
|
||||
libusb.dereference(dev)
|
||||
c.libusb.dereference(dev)
|
||||
reterr = err
|
||||
continue
|
||||
}
|
||||
ret = append(ret, &Device{handle: handle, Desc: desc})
|
||||
ret = append(ret, &Device{handle: handle, ctx: c, Desc: desc})
|
||||
} else {
|
||||
libusb.dereference(dev)
|
||||
c.libusb.dereference(dev)
|
||||
}
|
||||
}
|
||||
return ret, reterr
|
||||
@@ -213,10 +219,11 @@ func (c *Context) OpenDeviceWithVIDPID(vid, pid ID) (*Device, error) {
|
||||
|
||||
// Close releases the Context and all associated resources.
|
||||
func (c *Context) Close() error {
|
||||
var ret error
|
||||
c.done <- struct{}{}
|
||||
if c.ctx != nil {
|
||||
libusb.exit(c.ctx)
|
||||
ret = c.libusb.exit(c.ctx)
|
||||
}
|
||||
c.ctx = nil
|
||||
return nil
|
||||
return ret
|
||||
}
|
||||
|
27
usb_test.go
27
usb_test.go
@@ -18,12 +18,13 @@ package gousb
|
||||
import "testing"
|
||||
|
||||
func TestOPenDevices(t *testing.T) {
|
||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
||||
_, done := newFakeLibusb()
|
||||
defer done()
|
||||
|
||||
c := NewContext()
|
||||
defer c.Close()
|
||||
t.Parallel()
|
||||
c := newContextWithImpl(newFakeLibusb())
|
||||
defer func() {
|
||||
if err := c.Close(); err != nil {
|
||||
t.Errorf("Context.Close(): %v", err)
|
||||
}
|
||||
}()
|
||||
c.Debug(0)
|
||||
|
||||
descs := []*DeviceDesc{}
|
||||
@@ -55,9 +56,13 @@ func TestOPenDevices(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOpenDeviceWithVIDPID(t *testing.T) {
|
||||
// Can't be parallelized, newFakeLibusb modifies a shared global state.
|
||||
_, done := newFakeLibusb()
|
||||
defer done()
|
||||
t.Parallel()
|
||||
ctx := newContextWithImpl(newFakeLibusb())
|
||||
defer func() {
|
||||
if err := ctx.Close(); err != nil {
|
||||
t.Errorf("Context.Close(): %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
for _, d := range []struct {
|
||||
vid, pid ID
|
||||
@@ -69,9 +74,7 @@ func TestOpenDeviceWithVIDPID(t *testing.T) {
|
||||
{0x9999, 0x0001, true},
|
||||
{0x9999, 0x0002, false},
|
||||
} {
|
||||
c := NewContext()
|
||||
defer c.Close()
|
||||
dev, err := c.OpenDeviceWithVIDPID(d.vid, d.pid)
|
||||
dev, err := ctx.OpenDeviceWithVIDPID(d.vid, d.pid)
|
||||
if (dev != nil) != d.exists {
|
||||
t.Errorf("OpenDeviceWithVIDPID(%s/%s): device != nil is %v, want %v", ID(d.vid), ID(d.pid), dev != nil, d.exists)
|
||||
}
|
||||
|
Reference in New Issue
Block a user