usb
Version:
Library to access USB devices
251 lines (195 loc) • 9 kB
text/coffeescript
assert = require('assert')
util = require('util')
usb = require('../').usb
getDeviceList = require('../').getDeviceList
findByIds = require('../').findByIds
findBySerialNumber = require('../').findBySerialNumber
Worker = require('worker_threads').Worker
if typeof gc is 'function'
# running with --expose-gc, do a sweep between tests so valgrind blames the right one
afterEach -> gc()
describe 'USB Module', ->
it 'should describe basic constants', ->
assert.notEqual(usb, undefined, "usb must be undefined")
assert.ok((usb.LIBUSB_CLASS_PER_INTERFACE != undefined), "Constants must be described")
assert.ok((usb.LIBUSB_ENDPOINT_IN == 128))
it 'should handle abuse without crashing', ->
assert.throws -> new usb.Device()
assert.throws -> usb.Device()
assert.throws -> usb.Device.prototype.open.call({})
describe 'setDebugLevel', ->
it 'should throw when passed invalid args', ->
assert.throws((-> usb.setDebugLevel()), TypeError)
assert.throws((-> usb.setDebugLevel(-1)), TypeError)
assert.throws((-> usb.setDebugLevel(5)), TypeError)
it 'should succeed with good args', ->
assert.doesNotThrow(-> usb.setDebugLevel(0))
describe 'getDeviceList', ->
it 'should return at least one device', ->
l = getDeviceList()
assert.ok((l.length > 0))
describe 'findByIds', ->
it 'should return an array with length > 0', ->
dev = findByIds(0x59e3, 0x0a23)
assert.ok(dev, "Demo device is not attached")
describe 'findBySerialNumber', ->
it 'should return a single device', ->
dev = findBySerialNumber('TEST_DEVICE')
assert.ok(dev, "Demo device is not attached")
describe 'Hotplug', ->
it 'should detect detach', (done) ->
usb.once 'detach', (d) ->
assert.equal(d.deviceDescriptor.idVendor, 0x59e3)
assert.equal(d.deviceDescriptor.idProduct, 0x0a23)
done()
console.log('\n--- DISCONNECT DEVICE ---\n')
it 'should detect attach', (done) ->
usb.once 'attach', (d) ->
assert.equal(d.deviceDescriptor.idVendor, 0x59e3)
assert.equal(d.deviceDescriptor.idProduct, 0x0a23)
done()
console.log('\n--- CONNECT DEVICE ---\n')
describe 'Device', ->
device = null
before ->
device = findByIds(0x59e3, 0x0a23)
it 'should have sane properties', ->
assert.ok((device.busNumber > 0), "busNumber must be larger than 0")
assert.ok((device.deviceAddress > 0), "deviceAddress must be larger than 0")
if process.platform != 'darwin' || process.arch != 'arm64'
assert.ok((util.isArray(device.portNumbers)), "portNumbers must be an array")
it 'should have a deviceDescriptor property', ->
assert.ok(((deviceDesc = device.deviceDescriptor) != undefined))
it 'should have a configDescriptor property', ->
assert.ok(device.configDescriptor != undefined)
it 'should open', ->
device.open()
it 'gets string descriptors', (done) ->
device.getStringDescriptor device.deviceDescriptor.iManufacturer, (e, s) ->
assert.ok(e == undefined, e)
assert.equal(s, 'Nonolith Labs')
done()
it 'supports null string descriptors', (done) ->
device.getStringDescriptor device.configDescriptor.iConfiguration, (e, s) ->
assert.ok(e == undefined, e)
assert.equal(s, undefined)
done()
describe 'control transfer', ->
b = Buffer.from([0x30...0x40])
it 'should OUT transfer when the IN bit is not set', (done) ->
device.controlTransfer 0x40, 0x81, 0, 0, b, (e) ->
assert.ok(e == undefined, e)
done()
it 'should fail when bmRequestType doesn\'t match buffer / length', ->
assert.throws(-> device.controlTransfer(0x40, 0x81, 0, 0, 64))
it 'should IN transfer when the IN bit is set', (done) ->
device.controlTransfer 0xc0, 0x81, 0, 0, 128, (e, d) ->
#console.log("ControlTransferIn", d, e)
assert.ok(e == undefined, e)
assert.equal(d.toString(), b.toString())
done()
it 'should signal errors', (done) ->
device.controlTransfer 0xc0, 0xff, 0, 0, 64, (e, d) ->
assert.equal e.errno, usb.LIBUSB_TRANSFER_STALL
done()
describe 'Interface', ->
iface = null
before ->
iface = device.interfaces[0]
iface.claim()
it 'should have one interface', ->
assert.notEqual(iface, undefined)
it 'should be the same as the interfaceNo 0', ->
assert.strictEqual iface, device.interface(0)
if process.platform == 'linux'
it "shouldn't have a kernel driver", ->
assert.equal iface.isKernelDriverActive(), false
it "should fail to detach the kernel driver", ->
assert.throws -> iface.detachKernelDriver()
it "should fail to attach the kernel driver", ->
assert.throws -> iface.attachKernelDriver()
describe 'IN endpoint', ->
inEndpoint = null
before ->
inEndpoint = iface.endpoints[0]
it 'should be able to get the endpoint', ->
assert.ok inEndpoint?
it 'should be able to get the endpoint by address', ->
assert.equal(inEndpoint, iface.endpoint(0x81))
it 'should have the IN direction flag', ->
assert.equal(inEndpoint.direction, 'in')
it 'should have a descriptor', ->
assert.equal(inEndpoint.descriptor.bEndpointAddress, 0x81)
assert.equal(inEndpoint.descriptor.wMaxPacketSize, 64)
it 'should fail to write', ->
assert.throws -> inEndpoint.transfer(b)
it 'should support read', (done) ->
inEndpoint.transfer 64, (e, d) ->
assert.ok(e == undefined, e)
assert.ok(d.length == 64)
done()
it 'times out', (done) ->
iface.endpoints[2].timeout = 20
iface.endpoints[2].transfer 64, (e, d) ->
assert.equal e.errno, usb.LIBUSB_TRANSFER_TIMED_OUT
done()
it 'polls the device using events', (done) ->
pkts = 0
inEndpoint.startPoll 8, 64
inEndpoint.on 'data', (d) ->
assert.equal d.length, 64
pkts++
if pkts == 100
inEndpoint.stopPoll()
inEndpoint.once 'error', (e) ->
throw e
inEndpoint.once 'end', ->
done()
it 'polls the device using a callback', (done) ->
pkts = 0
inEndpoint.startPoll 8, 64, (e, b, a, c) ->
assert.equal(c, true)
assert.ok(e == undefined, e)
assert.equal(pkts, 100)
done()
inEndpoint.on 'data', (d) ->
assert.equal d.length, 64
pkts++
if pkts == 100
inEndpoint.stopPoll()
inEndpoint.once 'error', (e) ->
throw e
inEndpoint.once 'end', ->
assert.equal(pkts, 100)
describe 'OUT endpoint', ->
outEndpoint = null
before ->
outEndpoint = iface.endpoints[1]
it 'should be able to get the endpoint', ->
assert.ok outEndpoint?
it 'should be able to get the endpoint by address', ->
assert.equal(outEndpoint, iface.endpoint(0x02))
it 'should have the OUT direction flag', ->
assert.equal(outEndpoint.direction, 'out')
it 'should support write', (done) ->
outEndpoint.transfer [1,2,3,4], (e) ->
assert.ok(e == undefined, e)
done()
it 'times out', (done) ->
iface.endpoints[3].timeout = 20
iface.endpoints[3].transfer [1,2,3,4], (e) ->
assert.equal e.errno, usb.LIBUSB_TRANSFER_TIMED_OUT
done()
after (cb) ->
iface.release(cb)
after ->
device.close()
if process.platform != 'win32'
describe 'Context Aware', ->
it 'should handle opening the same device from different contexts', ->
for n in [1..5]
worker = new Worker('./test/worker.cjs')
worker.on 'message', (serial) ->
assert.equal(serial, 'TEST_DEVICE')
worker.on 'exit', (code) ->
assert.equal(code, 0)