@huddly/device-api-usb
Version:
Huddly SDK device api which uses node-usb wrapper responsible for handling the transport layer of the communication and discovering the physical device/camera
312 lines • 15.3 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const sinon_1 = __importDefault(require("sinon"));
const chai_1 = __importStar(require("chai"));
const chai_as_promised_1 = __importDefault(require("chai-as-promised"));
const mocha_param_1 = __importDefault(require("mocha-param"));
const sinon_chai_1 = __importDefault(require("sinon-chai"));
const usb_1 = require("usb");
const manager_1 = __importDefault(require("./../src/manager"));
const events_1 = require("events");
const HuddlyHex_1 = __importDefault(require("@huddly/sdk-interfaces/lib/enums/HuddlyHex"));
chai_1.default.should();
chai_1.default.use(sinon_chai_1.default);
chai_1.default.use(chai_as_promised_1.default);
chai_1.default.use(require('chai-things'));
const mockedDevices = [
{
getStringDescriptor: (idx, cb) => {
if (idx === 1)
cb(undefined, '4D000042');
else
cb(undefined, 'Huddly IQ');
},
deviceDescriptor: {
idVendor: HuddlyHex_1.default.VID,
idProduct: HuddlyHex_1.default.BOXFISH_PID,
iSerialNumber: 1,
iProduct: 2,
},
busNumber: 1,
deviceAddress: 2,
portNumbers: [1, 2],
interfaces: [],
open: () => { },
close: () => { },
serialNumber: '',
id: '',
productName: '',
productId: undefined,
vendorId: undefined
},
{
getStringDescriptor: (idx, cb) => {
if (idx === 1)
cb(undefined, '4D000043');
else
cb(undefined, 'Huddly IQ');
},
deviceDescriptor: {
idVendor: HuddlyHex_1.default.VID,
idProduct: HuddlyHex_1.default.BOXFISH_PID,
iSerialNumber: 1,
iProduct: 2,
},
busNumber: 1,
deviceAddress: 2,
portNumbers: [1, 3],
interfaces: [],
open: () => { },
close: () => { },
serialNumber: '',
id: '',
productName: '',
productId: undefined,
vendorId: undefined
},
{
getStringDescriptor: (idx, cb) => {
if (idx === 1)
cb(undefined, 'ABCDSF');
else
cb(undefined, 'Non Huddly Device');
},
deviceDescriptor: {
idVendor: 0x2bd1,
idProduct: 0x22,
iSerialNumber: 1,
iProduct: 2,
},
busNumber: 3,
deviceAddress: 2,
portNumbers: [1, 3],
open: () => { },
close: () => { },
serialNumber: 'ABCDSF'
}
];
describe('HuddlyUsbDeviceManager', () => {
let usbDeviceListStub;
let devicemanager;
beforeEach(() => {
const bar = () => {
console.log('bar');
return mockedDevices;
};
usbDeviceListStub = sinon_1.default.stub(manager_1.default.prototype, 'getUnfilteredDeviceList');
usbDeviceListStub.returns(mockedDevices);
devicemanager = new manager_1.default();
});
afterEach(() => {
usbDeviceListStub.restore();
});
describe('#generateUsbUniqueId', () => {
it('should generate a unique id based on device descriptor properties', () => {
const deviceOneHash = devicemanager.generateUsbUniqueId({
usbBusNumber: mockedDevices[0].busNumber,
usbDeviceAddress: mockedDevices[0].deviceAddress,
usbPortNumbers: mockedDevices[0].portNumbers
});
(0, chai_1.expect)(deviceOneHash).to.equal('46790582');
});
});
describe('#getDeviceUUID', () => {
it('should find cached device in attachedDevices list', () => {
const uuid = devicemanager.getDeviceUUID(mockedDevices[0]);
(0, chai_1.expect)(uuid).to.equals('46790582');
});
});
describe('#fetchAndPopulateDeviceParams', () => {
it('should fetch serial number and product number from device descriptor', () => __awaiter(void 0, void 0, void 0, function* () {
const DUT = mockedDevices[0];
yield devicemanager.fetchAndPopulateDeviceParams(DUT);
const generatedId = devicemanager.generateUsbUniqueId({
usbBusNumber: DUT.busNumber,
usbDeviceAddress: DUT.deviceAddress,
usbPortNumbers: DUT.portNumbers
});
(0, chai_1.expect)(mockedDevices[0].id).to.equal(generatedId);
(0, chai_1.expect)(mockedDevices[0].serialNumber).to.equal('4D000042');
(0, chai_1.expect)(mockedDevices[0].productName).to.equal('Huddly IQ');
(0, chai_1.expect)(mockedDevices[0].productId).to.equal(DUT.deviceDescriptor.idProduct);
(0, chai_1.expect)(mockedDevices[0].vendorId).to.equal(DUT.deviceDescriptor.idVendor);
}));
it('should reject in case getStringDescriptor throws an error', () => {
const mockedDev = {
getStringDescriptor: (idx, cb) => cb('Ooops, you cant do this!', undefined),
deviceDescriptor: { iSerialNumber: 1 },
open: () => { }
};
const fetchPromise = devicemanager.fetchAndPopulateDeviceParams(mockedDev);
return (0, chai_1.expect)(fetchPromise).to.eventually.be.rejectedWith('Ooops, you cant do this!');
});
describe('allowed access errors', () => {
(0, mocha_param_1.default)('should return false when libusb error_no ${value} is thrown on open/claim', [usb_1.usb.LIBUSB_ERROR_ACCESS, usb_1.usb.LIBUSB_ERROR_BUSY], (value) => __awaiter(void 0, void 0, void 0, function* () {
const busyDevice = {
open: () => { throw { errno: value }; },
};
const deviceParamsFetched = devicemanager.fetchAndPopulateDeviceParams(busyDevice);
(0, chai_1.expect)(deviceParamsFetched).to.eventually.be.false;
}));
});
describe('other access errors', () => {
const unwantedErrors = [usb_1.usb.LIBUSB_ERROR_IO, usb_1.usb.LIBUSB_ERROR_INVALID_PARAM, usb_1.usb.LIBUSB_ERROR_NO_DEVICE, usb_1.usb.LIBUSB_ERROR_NOT_FOUND, usb_1.usb.LIBUSB_ERROR_TIMEOUT, usb_1.usb.LIBUSB_ERROR_NOT_SUPPORTED, usb_1.usb.LIBUSB_ERROR_OTHER];
(0, mocha_param_1.default)('should re-throw when libusb error_no ${value} is thrown on open/claim', unwantedErrors, (value) => __awaiter(void 0, void 0, void 0, function* () {
const busyDevice = {
open: () => { throw { errno: value }; },
};
const deviceParamsFetched = devicemanager.fetchAndPopulateDeviceParams(busyDevice);
(0, chai_1.expect)(deviceParamsFetched).to.eventually.be.false;
}));
});
});
describe('#registerForHotplugEvents', () => {
describe('#onAttach', () => {
let emitter;
beforeEach(() => {
emitter = new events_1.EventEmitter();
});
it('should emit USB_ATTACH when a huddly device is attached', () => {
const attachPromise = new Promise((resolve) => {
emitter.on('ATTACH', (device) => {
(0, chai_1.expect)(device.serialNumber).to.equal(mockedDevices[0].serialNumber);
(0, chai_1.expect)(device.productName).to.equal('Huddly IQ');
(0, chai_1.expect)(devicemanager.cachedDevices.length).to.equal(1);
resolve();
});
});
devicemanager.registerForHotplugEvents(emitter);
usb_1.usb.emit('attach', mockedDevices[0]);
return attachPromise;
});
it('should not emit USB_ATTACH when other devices are attached', () => __awaiter(void 0, void 0, void 0, function* () {
const attachSpy = sinon_1.default.spy();
emitter.on('ATTACH', attachSpy);
devicemanager.registerForHotplugEvents(emitter);
usb_1.usb.emit('attach', mockedDevices[2]);
(0, chai_1.expect)(attachSpy.callCount).to.equal(0);
(0, chai_1.expect)(devicemanager.cachedDevices.length).to.equal(0);
}));
it('should not call #fetchAndPopulateDeviceParams for non-huddly devices', () => __awaiter(void 0, void 0, void 0, function* () {
const spy = sinon_1.default.spy(devicemanager, 'fetchAndPopulateDeviceParams');
devicemanager.registerForHotplugEvents(emitter);
usb_1.usb.emit('attach', mockedDevices[2]);
(0, chai_1.expect)(spy.callCount).to.equal(0);
}));
});
describe('#onDetach', () => {
const emitter = new events_1.EventEmitter();
it('should emit USB_DETACH with unique id if the device was not cached', () => {
const detachPromise = new Promise((resolve) => {
emitter.on('DETACH', (deviceId) => {
(0, chai_1.expect)(deviceId.serialNumber).to.equal(mockedDevices[0].serialNumber);
resolve();
});
});
devicemanager.registerForHotplugEvents(emitter);
usb_1.usb.emit('detach', mockedDevices[0]);
return detachPromise;
});
it('should not emit USB_DETACH when other devices are detached', () => __awaiter(void 0, void 0, void 0, function* () {
const detachSpy = sinon_1.default.spy();
emitter.on('DETACH', detachSpy);
devicemanager.registerForHotplugEvents(emitter);
usb_1.usb.emit('detach', mockedDevices[2]);
(0, chai_1.expect)(detachSpy.callCount).to.equal(0);
}));
});
});
describe('#deviceList', () => {
it('should only return the huddly devices with fetched parameters', () => __awaiter(void 0, void 0, void 0, function* () {
const devices = yield devicemanager.deviceList();
(0, chai_1.expect)(devices.length).to.equal(2);
// 1st device
(0, chai_1.expect)(devices[0].serialNumber).to.equal('4D000042');
(0, chai_1.expect)(devices[0].id).to.equal('46790582');
(0, chai_1.expect)(devices[0].productName).to.equal('Huddly IQ');
(0, chai_1.expect)(devices[0].productId).to.equal(HuddlyHex_1.default.BOXFISH_PID);
(0, chai_1.expect)(devices[0].vendorId).to.equal(HuddlyHex_1.default.VID);
// 2nd device
(0, chai_1.expect)(devices[1].serialNumber).to.equal('4D000043');
(0, chai_1.expect)(devices[1].id).to.equal('46790583');
(0, chai_1.expect)(devices[1].productName).to.equal('Huddly IQ');
(0, chai_1.expect)(devices[1].productId).to.equal(HuddlyHex_1.default.BOXFISH_PID);
(0, chai_1.expect)(devices[1].vendorId).to.equal(HuddlyHex_1.default.VID);
}));
it('should update cache with new devices', () => __awaiter(void 0, void 0, void 0, function* () {
const devices = yield devicemanager.deviceList();
(0, chai_1.expect)(devices.length).to.equals(2);
(0, chai_1.expect)(devicemanager.cachedDevices.length).to.equal(2);
}));
});
describe('#getDevice', () => {
it('should return null when serial does not exist', () => __awaiter(void 0, void 0, void 0, function* () {
const device = yield devicemanager.getDevice('AAAAA');
(0, chai_1.expect)(device).to.not.exist;
}));
it('should return first discovered device when serial number is not specified', () => __awaiter(void 0, void 0, void 0, function* () {
const device = yield devicemanager.getDevice();
(0, chai_1.expect)(device.serialNumber).to.equal(mockedDevices[0].serialNumber);
}));
it('should return the specific attachedDevice when serial number provided', () => __awaiter(void 0, void 0, void 0, function* () {
const device = yield devicemanager.getDevice(mockedDevices[1].serialNumber);
(0, chai_1.expect)(device.serialNumber).to.equal(mockedDevices[1].serialNumber);
}));
});
describe('#isValidHuddlyDevice', () => {
it('should return true if huddly vid and valid pid', () => {
const usbDev = {
deviceDescriptor: { idProduct: HuddlyHex_1.default.BOXFISH_PID, idVendor: HuddlyHex_1.default.VID },
};
(0, chai_1.expect)(devicemanager.isValidHuddlyDevice(usbDev)).to.equal(true);
});
it('should return false if huddly vid and invalid pid', () => {
const usbDev = {
deviceDescriptor: { idProduct: HuddlyHex_1.default.BASE_PID, idVendor: HuddlyHex_1.default.VID },
};
(0, chai_1.expect)(devicemanager.isValidHuddlyDevice(usbDev)).to.equal(false);
});
it('should return false if not vid and valid pid', () => {
const usbDev = {
deviceDescriptor: { idProduct: HuddlyHex_1.default.BOXFISH_PID, idVendor: 1000 },
};
(0, chai_1.expect)(devicemanager.isValidHuddlyDevice(usbDev)).to.equal(false);
});
});
});
//# sourceMappingURL=manager.spec.js.map