UNPKG

@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
"use strict"; 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