251 lines
8.8 KiB
CoffeeScript
251 lines
8.8 KiB
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)
|