@stoprocent/noble
Version:
A Node.js BLE (Bluetooth Low Energy) central library.
1,349 lines (1,107 loc) • 74.3 kB
JavaScript
const should = require('should');
const sinon = require('sinon');
const proxyquire = require('proxyquire').noCallThru();
const { assert } = sinon;
describe('hci-socket hci', () => {
const deviceId = 'deviceId';
const mockSocket = sinon.stub();
// Mock the crypto module
jest.mock('os', () => ({
platform: () => 'linux',
release: () => '5.10.0-11-amd64'
}));
jest.mock('@stoprocent/bluetooth-hci-socket', () => ({
loadDriver: () => mockSocket
}));
const Hci = require('../../../lib/hci-socket/hci');
let hci;
beforeEach(() => {
mockSocket.prototype.on = sinon.stub();
mockSocket.prototype.bindUser = sinon.stub();
mockSocket.prototype.bindRaw = sinon.stub();
mockSocket.prototype.start = sinon.stub();
mockSocket.prototype.isDevUp = sinon.stub();
mockSocket.prototype.removeAllListeners = sinon.stub();
mockSocket.prototype.setFilter = sinon.stub();
mockSocket.prototype.setAddress = sinon.stub();
mockSocket.prototype.write = sinon.stub();
hci = new Hci({});
hci._deviceId = deviceId;
});
afterEach(() => {
sinon.reset();
});
describe('init', () => {
it('should reset', () => {
hci.reset = sinon.spy();
hci._userChannel = 'userChannel';
hci.init();
assert.callCount(hci._socket.on, 3);
assert.calledWithMatch(hci._socket.on, 'data', sinon.match.func);
assert.calledWithMatch(hci._socket.on, 'error', sinon.match.func);
assert.calledWithMatch(hci._socket.on, 'state', sinon.match.func);
assert.calledOnceWithExactly(hci._socket.bindUser, deviceId, undefined);
assert.calledOnceWithExactly(hci._socket.start);
assert.calledOnceWithExactly(hci.reset);
});
it('should bindRaw', () => {
hci.pollIsDevUp = sinon.spy();
hci._userChannel = undefined;
hci._bound = false;
hci.init();
assert.callCount(hci._socket.on, 3);
assert.calledWithMatch(hci._socket.on, 'data', sinon.match.func);
assert.calledWithMatch(hci._socket.on, 'error', sinon.match.func);
assert.calledWithMatch(hci._socket.on, 'state', sinon.match.func);
assert.calledOnceWithExactly(hci._socket.bindRaw, deviceId, undefined);
assert.calledOnceWithExactly(hci._socket.start);
assert.calledOnceWithExactly(hci.pollIsDevUp);
should(hci._bound).be.true();
});
it('should not bindRaw', () => {
hci.pollIsDevUp = sinon.spy();
hci._userChannel = undefined;
hci._bound = true;
hci.init();
assert.callCount(hci._socket.on, 3);
assert.calledWithMatch(hci._socket.on, 'data', sinon.match.func);
assert.calledWithMatch(hci._socket.on, 'error', sinon.match.func);
assert.calledWithMatch(hci._socket.on, 'state', sinon.match.func);
assert.notCalled(hci._socket.bindRaw);
assert.calledOnceWithExactly(hci._socket.start);
assert.calledOnceWithExactly(hci.pollIsDevUp);
should(hci._bound).be.true();
});
});
describe('pollIsDevUp', () => {
let callback;
beforeEach(() => {
sinon.useFakeTimers();
callback = sinon.spy();
hci.setSocketFilter = sinon.spy();
hci.setEventMask = sinon.spy();
hci.setLeEventMask = sinon.spy();
hci.readLocalVersion = sinon.spy();
hci.writeLeHostSupported = sinon.spy();
hci.readLeHostSupported = sinon.spy();
hci.readLeBufferSize = sinon.spy();
hci.readBdAddr = sinon.spy();
hci.init = sinon.spy();
hci.readLeSupportedFeatures = sinon.spy();
hci.setCodedPhySupport = sinon.spy();
hci.on('stateChange', callback);
});
afterEach(() => {
sinon.restore();
sinon.reset();
});
it('should only register timeout', () => {
hci._socket.isDevUp.returns(true);
hci._isDevUp = true;
hci.pollIsDevUp();
assert.notCalled(hci.setSocketFilter);
assert.notCalled(hci.setEventMask);
assert.notCalled(hci.setLeEventMask);
assert.notCalled(hci.readLocalVersion);
assert.notCalled(hci.writeLeHostSupported);
assert.notCalled(hci.readLeHostSupported);
assert.notCalled(hci.readLeBufferSize);
assert.notCalled(hci.readBdAddr);
assert.notCalled(hci.setCodedPhySupport);
assert.notCalled(hci.init);
assert.notCalled(callback);
});
it('should re-init', () => {
hci._socket.isDevUp.returns(true);
hci._isDevUp = false;
hci._state = 'poweredOff';
hci.pollIsDevUp();
should(hci._state).equal(null);
assert.calledOnceWithExactly(hci._socket.removeAllListeners);
assert.calledOnceWithExactly(hci.init);
assert.notCalled(hci.setSocketFilter);
assert.notCalled(hci.setEventMask);
assert.notCalled(hci.setLeEventMask);
assert.notCalled(hci.readLocalVersion);
assert.notCalled(hci.writeLeHostSupported);
assert.notCalled(hci.readLeHostSupported);
assert.notCalled(hci.readLeBufferSize);
assert.notCalled(hci.readBdAddr);
assert.notCalled(hci.setCodedPhySupport);
assert.notCalled(callback);
});
it('should init all', () => {
hci._socket.isDevUp.returns(true);
hci._isDevUp = false;
hci._state = undefined;
hci.pollIsDevUp();
assert.calledOnceWithExactly(hci.setSocketFilter);
assert.calledOnceWithExactly(hci.readLeSupportedFeatures);
assert.notCalled(hci.setCodedPhySupport);
assert.notCalled(hci._socket.removeAllListeners);
assert.notCalled(hci.init);
assert.notCalled(callback);
});
it('should init all with extended option', () => {
hci._socket.isDevUp.returns(true);
hci._isDevUp = false;
hci._state = undefined;
hci._isExtended = true;
hci.pollIsDevUp();
assert.calledOnceWithExactly(hci.setSocketFilter);
assert.calledOnceWithExactly(hci.readLeSupportedFeatures);
assert.notCalled(hci._socket.removeAllListeners);
assert.notCalled(hci.init);
assert.notCalled(callback);
});
it('should emit stateChange', () => {
hci._socket.isDevUp.returns(false);
hci._isDevUp = true;
hci.pollIsDevUp();
assert.calledOnceWithExactly(callback, 'poweredOff');
assert.notCalled(hci._socket.removeAllListeners);
assert.notCalled(hci.init);
assert.notCalled(hci.setSocketFilter);
assert.notCalled(hci.setEventMask);
assert.notCalled(hci.setLeEventMask);
assert.notCalled(hci.readLocalVersion);
assert.notCalled(hci.writeLeHostSupported);
assert.notCalled(hci.readLeHostSupported);
assert.notCalled(hci.readLeBufferSize);
assert.notCalled(hci.readBdAddr);
assert.notCalled(hci.setCodedPhySupport);
});
});
it('should write codedPhySupport command', () => {
hci.setCodedPhySupport();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x31, 0x20, 0x03, 0x00, 0x05, 0x05]));
});
it('should setSocketFilter', () => {
hci.setSocketFilter();
assert.calledOnceWithExactly(hci._socket.setFilter, Buffer.from([0x16, 0, 0, 0, 0x20, 0xc1, 0x08, 0, 0, 0, 0, 0x40, 0, 0]));
});
it('should setEventMask', () => {
hci.setEventMask();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 1, 0x0c, 0x08, 0xff, 0xff, 0xfb, 0xff, 0x07, 0xf8, 0xbf, 0x3d]));
});
it('should reset', () => {
hci.reset();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 3, 0x0c, 0]));
});
it('should readSupportedCommands', () => {
hci.readSupportedCommands();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x02, 0x10, 0x00]));
});
it('should readLocalVersion', () => {
hci.readLocalVersion();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 1, 0x10, 0]));
});
it('should readBufferSize', () => {
hci.readBufferSize();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 5, 0x10, 0]));
});
it('should readBdAddr', () => {
hci.readBdAddr();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 9, 0x10, 0]));
});
describe('setAddress', () => {
it('should write vendor specific (Linux Foundation) command based on read local version response', () => {
hci.readBdAddr = sinon.spy();
hci.setScanEnabled = sinon.spy();
hci.setScanParameters = sinon.spy();
const cmd = 4097;
const status = 0;
// hciVer=12, hciRev=0, lmpVer=12, manufacturer=1521, lmpSubVer=65535
const result = Buffer.from([0x0C, 0x00, 0x00, 0x0C, 0xF1, 0x05, 0xFF, 0xFF]);
hci.processCmdCompleteEvent(cmd, status, result);
hci.setAddress('11:22:33:44:55:66');
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x06, 0xfc, 0x06, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11]));
});
it('should write vendor specific (Ericsson) command based on manufacturer value (', () => {
hci._manufacturer = 0;
hci.readBdAddr = sinon.spy();
hci.setAddress('11:22:33:44:55:66');
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x0d, 0xfc, 0x06, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11]));
});
it('should write vendor specific (Texas Instrument) command based on manufacturer value', () => {
hci._manufacturer = 13;
hci.readBdAddr = sinon.spy();
hci.setAddress('11:22:33:44:55:66');
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x06, 0xfc, 0x06, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11]));
});
it('should write vendor specific (BCM) command based on manufacturer value', () => {
hci._manufacturer = 15;
hci.readBdAddr = sinon.spy();
hci.setAddress('11:22:33:44:55:66');
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x01, 0xfc, 0x06, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11]));
});
it('should not write vendor specific command', () => {
hci.setAddress('11:22:33:44:55:66');
assert.notCalled(hci._socket.write);
});
});
describe('setLeEventMask', () => {
it('should setLeEventMask', () => {
hci.setLeEventMask();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 1, 0x20, 8, 0x1f, 0, 0, 0, 0, 0, 0, 0]));
});
it('should setLeEventMask for BLE5 (extended)', () => {
hci._isExtended = true;
hci.setLeEventMask();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 1, 0x20, 8, 0x1f, 0xff, 0, 0, 0, 0, 0, 0]));
});
});
it('should readLeBufferSize', () => {
hci.readLeBufferSize();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 2, 0x20, 0]));
});
it('should readLeHostSupported', () => {
hci.readLeHostSupported();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 0x6c, 0x0c, 0]));
});
it('should writeLeHostSupported', () => {
hci.writeLeHostSupported();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 0x6d, 0x0c, 2, 1, 0]));
});
describe('setScanParameters', () => {
it('should keep default parameters', () => {
hci.setScanParameters();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 0x0b, 0x20, 7, 1, 0x12, 0, 0x12, 0, 0, 0]));
});
it('should keep default parameters (extended)', () => {
hci._isExtended = true;
hci.setScanParameters();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 0x41, 0x20, 0x0d, 0x00, 0x00, 0x05, 0x01, 0x12, 0x00, 0x12, 0x00, 0x01, 0x12, 0x00, 0x12, 0x00]));
});
it('should force parameters', () => {
hci.setScanParameters(0x2222, 0x3333);
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 0x0b, 0x20, 7, 1, 0x22, 0x022, 0x33, 0x33, 0, 0]));
});
it('should force parameters (extended)', () => {
hci._isExtended = true;
hci.setScanParameters(0x2222, 0x3333);
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 0x41, 0x20, 0x0d, 0x00, 0x00, 0x05, 0x01, 0x22, 0x22, 0x33, 0x33, 0x01, 0x22, 0x22, 0x33, 0x33]));
});
});
describe('setScanEnabled', () => {
it('should keep default parameters', () => {
hci.setScanEnabled();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 0x0c, 0x20, 2, 0, 0]));
});
it('should keep default parameters (extended)', () => {
hci._isExtended = true;
hci.setScanEnabled();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 0x42, 0x20, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
});
it('should force parameters', () => {
hci.setScanEnabled(true, true);
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 0x0c, 0x20, 2, 1, 1]));
});
it('should force parameters (extended)', () => {
hci._isExtended = true;
hci.setScanEnabled(true, true);
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([1, 0x42, 0x20, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00]));
});
});
describe('createLeConn', () => {
it('should emit reset event and call createLeConnAfterReset', () => {
const address = 'aa:bb:cc:dd:ee:ff';
const addressType = 'random';
const parameters = { minInterval: 0x0060, maxInterval: 0x00c0 };
hci.createLeConnAfterReset = jest.fn();
hci.createLeConn(address, addressType, parameters);
hci.emit('reset');
expect(hci.createLeConnAfterReset).toHaveBeenCalledWith(
address, addressType, parameters
);
});
});
describe('createLeConnAfterReset', () => {
it('should keep default parameters', () => {
const address = 'aa:bb:cc:dd:ee';
const addressType = 'random';
hci.createLeConnAfterReset(address, addressType);
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x0d, 0x20, 0x19, 0x60, 0x00, 0x30, 0x00, 0x00, 0x01, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x00, 0x00, 0x06, 0x00, 0x12, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x04, 0x00, 0x06, 0x00]));
});
it('should keep default parameters (extended)', () => {
const address = 'aa:bb:cc:dd:ee';
const addressType = 'random';
hci._isExtended = true;
hci.createLeConnAfterReset(address, addressType);
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x43, 0x20, 0x2a, 0x00, 0x00, 0x01, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x00, 0x05, 0x60, 0x00, 0x60, 0x00, 0x06, 0x00, 0x12, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 0x06, 0x00, 0x12, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00]));
});
it('should override default parameters', () => {
const address = 'ee:dd:cc:bb:aa';
const addressType = 'not_random';
const parameters = { minInterval: 0x0060, maxInterval: 0x00c0, latency: 0x0010, timeout: 0x0c80 };
hci.createLeConnAfterReset(address, addressType, parameters);
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x0d, 0x20, 0x19, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x00, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x10, 0x00, 0x80, 0x0c, 0x04, 0x00, 0x06, 0x00]));
});
it('should override default parameters (extended)', () => {
const address = 'ee:dd:cc:bb:aa';
const addressType = 'not_random';
const parameters = { minInterval: 0x0060, maxInterval: 0x00c0, latency: 0x0010, timeout: 0x0c80 };
hci._isExtended = true;
hci.createLeConnAfterReset(address, addressType, parameters);
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x43, 0x20, 0x2a, 0x00, 0x00, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x00, 0x05, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x10, 0x00, 0x80, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x10, 0x00, 0x80, 0x0c, 0x00, 0x00, 0x00, 0x00]));
});
});
it('should write connUpdateLe', () => {
const handle = 0x1234;
const minInterval = 5;
const maxInterval = 15;
const latency = 12;
const supervisionTimeout = 25;
hci.connUpdateLe(handle, minInterval, maxInterval, latency, supervisionTimeout);
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x13, 0x20, 0x0e, 0x34, 0x12, 0x04, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00]));
});
it('should write cancelConnect', () => {
hci.cancelConnect();
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x0e, 0x20, 0x00]));
});
it('should write startLeEncryption', () => {
const handle = 0x1234;
const random = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]);
const diversifier = Buffer.from([11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 23, 24, 25]);
const key = Buffer.from([31, 32, 33, 34, 35, 36, 37, 38, 39, 30, 31, 32, 33, 33, 34, 35]);
hci.startLeEncryption(handle, random, diversifier, key);
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x19, 0x20, 0x1c, 0x34, 0x12, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0b, 0x0c, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x1e, 0x1f, 0x20, 0x21, 0x21, 0x22, 0x23]));
});
describe('disconnect', () => {
it('should write disconnect with defaults', () => {
const handle = 0x1234;
const reason = undefined;
hci.disconnect(handle, reason);
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x06, 0x04, 0x03, 0x34, 0x12, 0x13]));
});
it('should write disconnect with reason', () => {
const handle = 0x1234;
const reason = 17;
hci.disconnect(handle, reason);
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x06, 0x04, 0x03, 0x34, 0x12, 0x11]));
});
});
it('should write readRssi', () => {
const handle = 0x1234;
hci.readRssi(handle);
assert.calledOnceWithExactly(hci._socket.write, Buffer.from([0x01, 0x05, 0x14, 0x02, 0x34, 0x12]));
});
it('should writeAclDataPkt - push in aclQueue and flushAcl', async () => {
hci.flushAcl = sinon.spy();
hci._aclBuffers = [1, 2, 3, 4, 5, 6, 7, 8];
const handle = 0x1234;
const cid = 345;
const data = Buffer.from([5, 6, 7, 8, 9, 10, 11]);
await hci.writeAclDataPkt(handle, cid, data);
assert.calledOnceWithExactly(hci.flushAcl);
should(hci._aclQueue).deepEqual([
{
handle: 4660,
packet: Buffer.from([0x02, 0x34, 0x12, 0x08, 0x00, 0x07, 0x00, 0x59, 0x01, 0x05, 0x06, 0x07, 0x08])
},
{
handle: 4660,
packet: Buffer.from([0x02, 0x34, 0x12, 0x03, 0x00, 0x09, 0x0a, 0x0b])
}
]);
});
describe('flushAcl', () => {
it('should not write flush on no pending connections', () => {
const queue = [
{
handle: 4660,
packet: Buffer.from([0x02, 0x34, 0x12, 0x08, 0x00, 0x07, 0x00, 0x59, 0x01, 0x05, 0x06, 0x07, 0x08])
},
{
handle: 4660,
packet: Buffer.from([0x02, 0x34, 0x12, 0x03, 0x00, 0x09, 0x0a, 0x0b])
}
];
hci._aclQueue = [...queue];
hci.flushAcl();
assert.notCalled(hci._socket.write);
should(hci._aclQueue).deepEqual(queue);
});
it('should not write flush on empty queue', () => {
hci._aclQueue = [];
hci._aclConnections.set(4660, { pending: 3 });
hci._aclConnections.set(4661, { pending: 2 });
hci._aclBuffers = { num: 12 };
hci.flushAcl();
assert.notCalled(hci._socket.write);
should(hci._aclQueue).be.empty();
});
it('should not write flush on not enough pending connections', () => {
const queue = [
{
handle: 4660,
packet: Buffer.from([0x02, 0x34, 0x12, 0x08, 0x00, 0x07, 0x00, 0x59, 0x01, 0x05, 0x06, 0x07, 0x08])
},
{
handle: 4660,
packet: Buffer.from([0x02, 0x34, 0x12, 0x03, 0x00, 0x09, 0x0a, 0x0b])
}
];
hci._aclQueue = [...queue];
hci._aclConnections.set(4660, { pending: 3 });
hci._aclConnections.set(4661, { pending: 2 });
hci._aclBuffers = { num: 1 };
hci.flushAcl();
assert.notCalled(hci._socket.write);
should(hci._aclQueue).deepEqual(queue);
});
it('should write flush', async () => {
const queue = [
{
handle: 4660,
packet: Buffer.from([0x02, 0x34, 0x12, 0x08, 0x00, 0x07, 0x00, 0x59, 0x01, 0x05, 0x06, 0x07, 0x08])
},
{
handle: 4660,
packet: Buffer.from([0x02, 0x34, 0x12, 0x03, 0x00, 0x09, 0x0a, 0x0b])
},
{
handle: 4661,
packet: Buffer.from([0x02])
}
];
hci._aclQueue = [...queue];
hci._aclConnections.set(4660, { pending: 3 });
hci._aclConnections.set(4661, { pending: 2 });
hci._aclBuffers = { num: 12 };
await hci.flushAcl();
assert.callCount(hci._socket.write, 3);
assert.calledWithExactly(hci._socket.write, Buffer.from([0x02, 0x34, 0x12, 0x08, 0x00, 0x07, 0x00, 0x59, 0x01, 0x05, 0x06, 0x07, 0x08]));
assert.calledWithExactly(hci._socket.write, Buffer.from([0x02, 0x34, 0x12, 0x03, 0x00, 0x09, 0x0a, 0x0b]));
assert.calledWithExactly(hci._socket.write, Buffer.from([0x02]));
should(hci._aclQueue).be.empty();
});
});
describe('onSocketData', () => {
// Define the standard ACL queue used in tests
const aclQueue = [
{
handle: 4660,
packet: Buffer.from([0x02, 0x34, 0x12, 0x08, 0x00, 0x07, 0x00, 0x59, 0x01, 0x05, 0x06, 0x07, 0x08])
},
{
handle: 4660,
packet: Buffer.from([0x02, 0x34, 0x12, 0x03, 0x00, 0x09, 0x0a, 0x0b])
},
{
handle: 4661,
packet: Buffer.from([0x02])
}
];
// Event callback mocks
let disconnCompleteCallback;
let encryptChangeCallback;
let aclDataPktCallback;
let leScanEnableSetCmdCallback;
beforeEach(() => {
// Setup spies on HCI methods - preserve actual implementation
jest.spyOn(hci, 'flushAcl');
jest.spyOn(hci, 'processCmdCompleteEvent');
jest.spyOn(hci, 'processCmdStatusEvent');
hci.processLeMetaEvent = jest.fn();
// Suppress console.warn
jest.spyOn(console, 'warn').mockImplementation(() => {});
// Setup HCI's internal state for testing
hci._aclQueue = [...aclQueue];
hci._aclConnections = new Map();
hci._aclConnections.set(4660, { pending: 3 });
hci._aclConnections.set(4661, { pending: 2 });
hci._handleBuffers = {};
// Setup event listener callbacks as mocks
disconnCompleteCallback = jest.fn();
encryptChangeCallback = jest.fn();
aclDataPktCallback = jest.fn();
leScanEnableSetCmdCallback = jest.fn();
// Register event handlers
hci.on('disconnComplete', disconnCompleteCallback);
hci.on('encryptChange', encryptChangeCallback);
hci.on('aclDataPkt', aclDataPktCallback);
hci.on('leScanEnableSetCmd', leScanEnableSetCmdCallback);
});
afterEach(() => {
// Clear all mocks between tests
jest.restoreAllMocks();
});
test('should flushAcl - HCI_EVENT_PKT / EVT_DISCONN_COMPLETE', () => {
const eventType = 4;
const subEventType = 5;
const data = Buffer.from([eventType, subEventType, 0, 0, 0x34, 0x12, 3]);
hci.onSocketData(data);
// Called
expect(hci.flushAcl).toHaveBeenCalledTimes(1);
expect(disconnCompleteCallback).toHaveBeenCalledWith(4660, 3);
// Not called
expect(hci.processCmdCompleteEvent).not.toHaveBeenCalled();
expect(hci.processLeMetaEvent).not.toHaveBeenCalled();
expect(encryptChangeCallback).not.toHaveBeenCalled();
expect(aclDataPktCallback).not.toHaveBeenCalled();
expect(leScanEnableSetCmdCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual([
{
handle: 4661,
packet: Buffer.from([0x02])
}
]);
expect(Array.from(hci._aclConnections.keys())).toEqual([4661]);
expect(hci._aclConnections.get(4661)).toEqual({ pending: 2 });
});
test('should only emit encryptChange - HCI_EVENT_PKT / EVT_ENCRYPT_CHANGE', () => {
const eventType = 4;
const subEventType = 8;
const data = Buffer.from([eventType, subEventType, 0, 0, 0x34, 0x12, 3]);
hci.onSocketData(data);
// Called
expect(encryptChangeCallback).toHaveBeenCalledWith(4660, 3);
// Not called
expect(hci.flushAcl).not.toHaveBeenCalled();
expect(hci.processCmdCompleteEvent).not.toHaveBeenCalled();
expect(hci.processLeMetaEvent).not.toHaveBeenCalled();
expect(disconnCompleteCallback).not.toHaveBeenCalled();
expect(aclDataPktCallback).not.toHaveBeenCalled();
expect(leScanEnableSetCmdCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual(aclQueue);
expect(Array.from(hci._aclConnections.keys())).toEqual(expect.arrayContaining([4660, 4661]));
expect(hci._aclConnections.get(4660)).toEqual({ pending: 3 });
expect(hci._aclConnections.get(4661)).toEqual({ pending: 2 });
});
test('should only processCmdCompleteEvent - HCI_EVENT_PKT / EVT_CMD_COMPLETE', () => {
const eventType = 4;
const subEventType = 14;
const data = Buffer.from([eventType, subEventType, 0, 0, 0x34, 0x12, 3, 9, 9]);
hci.onSocketData(data);
// Called
expect(hci.processCmdCompleteEvent).toHaveBeenCalledWith(4660, 3, Buffer.from([9, 9]));
// Not called
expect(hci.flushAcl).not.toHaveBeenCalled();
expect(encryptChangeCallback).not.toHaveBeenCalled();
expect(hci.processLeMetaEvent).not.toHaveBeenCalled();
expect(disconnCompleteCallback).not.toHaveBeenCalled();
expect(aclDataPktCallback).not.toHaveBeenCalled();
expect(leScanEnableSetCmdCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual(aclQueue);
expect(Array.from(hci._aclConnections.keys())).toEqual(expect.arrayContaining([4660, 4661]));
expect(hci._aclConnections.get(4660)).toEqual({ pending: 3 });
expect(hci._aclConnections.get(4661)).toEqual({ pending: 2 });
});
test('should only processCmdStatusEvent - HCI_EVENT_PKT / EVT_CMD_STATUS', () => {
const eventType = 4;
const subEventType = 15;
const data = Buffer.from([eventType, subEventType, 4, 2, 0x34, 0x12, 3, 9, 9]);
hci.onSocketData(data);
// Called
expect(hci.processCmdStatusEvent).toHaveBeenCalledWith(786, 2);
// Not called
expect(hci.flushAcl).not.toHaveBeenCalled();
expect(encryptChangeCallback).not.toHaveBeenCalled();
expect(hci.processCmdCompleteEvent).not.toHaveBeenCalled();
expect(hci.processLeMetaEvent).not.toHaveBeenCalled();
expect(disconnCompleteCallback).not.toHaveBeenCalled();
expect(aclDataPktCallback).not.toHaveBeenCalled();
expect(leScanEnableSetCmdCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual(aclQueue);
expect(Array.from(hci._aclConnections.keys())).toEqual(expect.arrayContaining([4660, 4661]));
expect(hci._aclConnections.get(4660)).toEqual({ pending: 3 });
expect(hci._aclConnections.get(4661)).toEqual({ pending: 2 });
});
test('should only processLeMetaEvent - HCI_EVENT_PKT / EVT_LE_META_EVENT', () => {
const eventType = 4;
const subEventType = 62;
const data = Buffer.from([eventType, subEventType, 0, 1, 0x34, 0x12, 3, 9, 9]);
hci.onSocketData(data);
// Called
expect(hci.processLeMetaEvent).toHaveBeenCalledWith(1, 52, Buffer.from([0x12, 3, 9, 9]));
// Not called
expect(hci.flushAcl).not.toHaveBeenCalled();
expect(encryptChangeCallback).not.toHaveBeenCalled();
expect(hci.processCmdCompleteEvent).not.toHaveBeenCalled();
expect(disconnCompleteCallback).not.toHaveBeenCalled();
expect(aclDataPktCallback).not.toHaveBeenCalled();
expect(leScanEnableSetCmdCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual(aclQueue);
expect(Array.from(hci._aclConnections.keys())).toEqual(expect.arrayContaining([4660, 4661]));
expect(hci._aclConnections.get(4660)).toEqual({ pending: 3 });
expect(hci._aclConnections.get(4661)).toEqual({ pending: 2 });
});
test('should only flushAcl - HCI_EVENT_PKT / EVT_NUMBER_OF_COMPLETED_PACKETS', () => {
const eventType = 4;
const subEventType = 19;
const data = Buffer.from([eventType, subEventType, 0, 1, 0x34, 0x12, 3, 9, 9]);
hci.onSocketData(data);
// Called
expect(hci.flushAcl).toHaveBeenCalled();
// Not called
expect(hci.processLeMetaEvent).not.toHaveBeenCalled();
expect(encryptChangeCallback).not.toHaveBeenCalled();
expect(hci.processCmdCompleteEvent).not.toHaveBeenCalled();
expect(disconnCompleteCallback).not.toHaveBeenCalled();
expect(aclDataPktCallback).not.toHaveBeenCalled();
expect(leScanEnableSetCmdCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual(aclQueue);
expect(Array.from(hci._aclConnections.keys())).toEqual(expect.arrayContaining([4660, 4661]));
expect(hci._aclConnections.get(4660)).toEqual({ pending: 0 });
expect(hci._aclConnections.get(4661)).toEqual({ pending: 2 });
});
test('should do nothing - HCI_EVENT_PKT / unknown subEventType', () => {
const eventType = 4;
const subEventType = 122;
const data = Buffer.from([eventType, subEventType, 0, 1, 0x34, 0x12, 3, 9, 9]);
hci.onSocketData(data);
// Not called - nothing should happen
expect(hci.flushAcl).not.toHaveBeenCalled();
expect(hci.processLeMetaEvent).not.toHaveBeenCalled();
expect(encryptChangeCallback).not.toHaveBeenCalled();
expect(hci.processCmdCompleteEvent).not.toHaveBeenCalled();
expect(disconnCompleteCallback).not.toHaveBeenCalled();
expect(aclDataPktCallback).not.toHaveBeenCalled();
expect(leScanEnableSetCmdCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual(aclQueue);
expect(Array.from(hci._aclConnections.keys())).toEqual(expect.arrayContaining([4660, 4661]));
expect(hci._aclConnections.get(4660)).toEqual({ pending: 3 });
expect(hci._aclConnections.get(4661)).toEqual({ pending: 2 });
});
test('should only emit aclDataPkt - HCI_ACLDATA_PKT / ACL_START', () => {
const eventType = 2;
const subEventTypeP1 = 0xf2;
const subEventTypeP2 = 0x24;
const data = Buffer.from([eventType, subEventTypeP1, subEventTypeP2, 0x34, 0x12, 0x03, 0x00, 3, 9, 9, 8, 7]);
hci.onSocketData(data);
// Called
expect(aclDataPktCallback).toHaveBeenCalledWith(1266, 2307, Buffer.from([9, 8, 7]));
// Not called
expect(hci.flushAcl).not.toHaveBeenCalled();
expect(hci.processLeMetaEvent).not.toHaveBeenCalled();
expect(encryptChangeCallback).not.toHaveBeenCalled();
expect(hci.processCmdCompleteEvent).not.toHaveBeenCalled();
expect(disconnCompleteCallback).not.toHaveBeenCalled();
expect(leScanEnableSetCmdCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual(aclQueue);
expect(Array.from(hci._aclConnections.keys())).toEqual(expect.arrayContaining([4660, 4661]));
expect(hci._aclConnections.get(4660)).toEqual({ pending: 3 });
expect(hci._aclConnections.get(4661)).toEqual({ pending: 2 });
expect(hci._handleBuffers).toEqual({});
});
test('should register handle buffer - HCI_ACLDATA_PKT / ACL_START with incomplete data', () => {
const eventType = 2;
const subEventTypeP1 = 0xf2;
const subEventTypeP2 = 0x24;
const data = Buffer.from([eventType, subEventTypeP1, subEventTypeP2, 0x34, 0x12, 0x03, 0x00, 3, 9, 9, 8]);
hci.onSocketData(data);
// Not called
expect(aclDataPktCallback).not.toHaveBeenCalled();
expect(hci.flushAcl).not.toHaveBeenCalled();
expect(hci.processLeMetaEvent).not.toHaveBeenCalled();
expect(encryptChangeCallback).not.toHaveBeenCalled();
expect(hci.processCmdCompleteEvent).not.toHaveBeenCalled();
expect(disconnCompleteCallback).not.toHaveBeenCalled();
expect(leScanEnableSetCmdCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual(aclQueue);
expect(Array.from(hci._aclConnections.keys())).toEqual(expect.arrayContaining([4660, 4661]));
expect(hci._aclConnections.get(4660)).toEqual({ pending: 3 });
expect(hci._aclConnections.get(4661)).toEqual({ pending: 2 });
// Check buffer was created properly
expect(hci._handleBuffers[1266]).toEqual({
length: 3,
cid: 2307,
data: expect.any(Buffer)
});
expect(Buffer.from(hci._handleBuffers[1266].data)).toEqual(Buffer.from([9, 8]));
});
test('should do nothing - HCI_ACLDATA_PKT / ACL_CONT without existing buffer', () => {
const eventType = 2;
const subEventTypeP1 = 0xf2;
const subEventTypeP2 = 0x14;
const data = Buffer.from([eventType, subEventTypeP1, subEventTypeP2, 0x34, 0x12, 0x03, 0x00, 3, 9, 9, 8]);
hci.onSocketData(data);
// Not called
expect(aclDataPktCallback).not.toHaveBeenCalled();
expect(hci.flushAcl).not.toHaveBeenCalled();
expect(hci.processLeMetaEvent).not.toHaveBeenCalled();
expect(encryptChangeCallback).not.toHaveBeenCalled();
expect(hci.processCmdCompleteEvent).not.toHaveBeenCalled();
expect(disconnCompleteCallback).not.toHaveBeenCalled();
expect(leScanEnableSetCmdCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual(aclQueue);
expect(Array.from(hci._aclConnections.keys())).toEqual(expect.arrayContaining([4660, 4661]));
expect(hci._aclConnections.get(4660)).toEqual({ pending: 3 });
expect(hci._aclConnections.get(4661)).toEqual({ pending: 2 });
expect(hci._handleBuffers).toEqual({});
});
test('should concat data - HCI_ACLDATA_PKT / ACL_CONT with existing buffer', () => {
const eventType = 2;
const subEventTypeP1 = 0xf2;
const subEventTypeP2 = 0x14;
const data = Buffer.from([eventType, subEventTypeP1, subEventTypeP2, 0x34, 0x12, 0x03, 0x00, 3, 9, 9, 8]);
// Setup pre-existing buffer
hci._handleBuffers = {
1266: {
length: 3,
cid: 2307,
data: Buffer.from([3, 4])
}
};
hci.onSocketData(data);
// Not called
expect(aclDataPktCallback).not.toHaveBeenCalled();
expect(hci.flushAcl).not.toHaveBeenCalled();
expect(hci.processLeMetaEvent).not.toHaveBeenCalled();
expect(encryptChangeCallback).not.toHaveBeenCalled();
expect(hci.processCmdCompleteEvent).not.toHaveBeenCalled();
expect(disconnCompleteCallback).not.toHaveBeenCalled();
expect(leScanEnableSetCmdCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual(aclQueue);
expect(Array.from(hci._aclConnections.keys())).toEqual(expect.arrayContaining([4660, 4661]));
expect(hci._aclConnections.get(4660)).toEqual({ pending: 3 });
expect(hci._aclConnections.get(4661)).toEqual({ pending: 2 });
// Check buffer was updated properly
expect(hci._handleBuffers).toEqual({
1266: {
length: 3,
cid: 2307,
data: expect.any(Buffer)
}
});
// Check buffer contents
const bufferData = hci._handleBuffers[1266].data;
expect(Buffer.from(bufferData)).toEqual(Buffer.from([3, 4, 3, 0, 3, 9, 9, 8]));
});
test('should concat data and emit aclDataPkt - HCI_ACLDATA_PKT / ACL_CONT when complete', () => {
const eventType = 2;
const subEventTypeP1 = 0xf2;
const subEventTypeP2 = 0x14;
const data = Buffer.from([eventType, subEventTypeP1, subEventTypeP2, 0x34, 0x12, 0x03, 0x00, 3, 9, 9, 8]);
// Setup pre-existing buffer with enough expected length to trigger completion
hci._handleBuffers = {
1266: {
length: 8,
cid: 2307,
data: Buffer.from([3, 4])
}
};
hci.onSocketData(data);
// Called
expect(aclDataPktCallback).toHaveBeenCalledWith(
1266,
2307,
expect.any(Buffer)
);
// Verify buffer contents in the callback
const callData = aclDataPktCallback.mock.calls[0][2];
expect(Buffer.from(callData)).toEqual(Buffer.from([3, 4, 3, 0, 3, 9, 9, 8]));
// Not called
expect(hci.flushAcl).not.toHaveBeenCalled();
expect(hci.processLeMetaEvent).not.toHaveBeenCalled();
expect(encryptChangeCallback).not.toHaveBeenCalled();
expect(hci.processCmdCompleteEvent).not.toHaveBeenCalled();
expect(disconnCompleteCallback).not.toHaveBeenCalled();
expect(leScanEnableSetCmdCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual(aclQueue);
expect(Array.from(hci._aclConnections.keys())).toEqual(expect.arrayContaining([4660, 4661]));
expect(hci._aclConnections.get(4660)).toEqual({ pending: 3 });
expect(hci._aclConnections.get(4661)).toEqual({ pending: 2 });
// Buffer should be emptied after processing
expect(hci._handleBuffers).toEqual({});
});
test('should emit leScanEnableSetCmd - HCI_COMMAND_PKT / LE_SET_SCAN_ENABLE_CMD', () => {
const eventType = 1;
const subEventTypeP1 = 0x0c;
const subEventTypeP2 = 0x20;
const data = Buffer.from([eventType, subEventTypeP1, subEventTypeP2, 0x34, 0x01, 0]);
hci.onSocketData(data);
// Called
expect(leScanEnableSetCmdCallback).toHaveBeenCalledWith(true, false);
// Not called
expect(hci.flushAcl).not.toHaveBeenCalled();
expect(hci.processLeMetaEvent).not.toHaveBeenCalled();
expect(encryptChangeCallback).not.toHaveBeenCalled();
expect(hci.processCmdCompleteEvent).not.toHaveBeenCalled();
expect(disconnCompleteCallback).not.toHaveBeenCalled();
expect(aclDataPktCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual(aclQueue);
expect(Array.from(hci._aclConnections.keys())).toEqual(expect.arrayContaining([4660, 4661]));
});
test('should not emit leScanEnableSetCmd - HCI_COMMAND_PKT / unknown command', () => {
const eventType = 1;
const subEventTypeP1 = 0x0c;
const subEventTypeP2 = 0x21; // Different command code
const data = Buffer.from([eventType, subEventTypeP1, subEventTypeP2, 0x34, 0x01, 0]);
hci.onSocketData(data);
// Not called
expect(leScanEnableSetCmdCallback).not.toHaveBeenCalled();
expect(hci.flushAcl).not.toHaveBeenCalled();
expect(hci.processLeMetaEvent).not.toHaveBeenCalled();
expect(encryptChangeCallback).not.toHaveBeenCalled();
expect(hci.processCmdCompleteEvent).not.toHaveBeenCalled();
expect(disconnCompleteCallback).not.toHaveBeenCalled();
expect(aclDataPktCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual(aclQueue);
expect(Array.from(hci._aclConnections.keys())).toEqual(expect.arrayContaining([4660, 4661]));
});
test('should do nothing - unknown event type', () => {
const eventType = 122; // Unknown event type
const subEventTypeP1 = 0x0c;
const subEventTypeP2 = 0x21;
const data = Buffer.from([eventType, subEventTypeP1, subEventTypeP2, 0x34, 0x01, 0]);
hci.onSocketData(data);
// Not called
expect(leScanEnableSetCmdCallback).not.toHaveBeenCalled();
expect(hci.flushAcl).not.toHaveBeenCalled();
expect(hci.processLeMetaEvent).not.toHaveBeenCalled();
expect(encryptChangeCallback).not.toHaveBeenCalled();
expect(hci.processCmdCompleteEvent).not.toHaveBeenCalled();
expect(disconnCompleteCallback).not.toHaveBeenCalled();
expect(aclDataPktCallback).not.toHaveBeenCalled();
// HCI state checks
expect(hci._aclQueue).toEqual(aclQueue);
expect(Array.from(hci._aclConnections.keys())).toEqual(expect.arrayContaining([4660, 4661]));
});
});
// LE_READ_LOCAL_SUPPORTED_FEATURES tests
describe('LE_READ_LOCAL_SUPPORTED_FEATURES', () => {
beforeEach(() => {
// Spy on methods rather than replacing them
jest.spyOn(hci, 'setCodedPhySupport');
jest.spyOn(hci, 'setEventMask');
jest.spyOn(hci, 'setLeEventMask');
jest.spyOn(hci, 'readLocalVersion');
jest.spyOn(hci, 'writeLeHostSupported');
jest.spyOn(hci, 'readLeHostSupported');
jest.spyOn(hci, 'readLeBufferSize');
jest.spyOn(hci, 'readBdAddr');
// Reset _isExtended flag
hci._isExtended = false;
});
afterEach(() => {
jest.clearAllMocks();
});
test('should not process on error status', () => {
const cmd = 8195;
const status = 1;
const result = Buffer.from([0x00, 0x00, 0x00, 0x00]);
hci.processCmdCompleteEvent(cmd, status, result);
// Verify no methods were called
expect(hci.setCodedPhySupport).not.toHaveBeenCalled();
expect(hci.setEventMask).not.toHaveBeenCalled();
expect(hci.setLeEventMask).not.toHaveBeenCalled();
expect(hci.readLocalVersion).not.toHaveBeenCalled();
expect(hci.writeLeHostSupported).not.toHaveBeenCalled();
expect(hci.readLeHostSupported).not.toHaveBeenCalled();
expect(hci.readLeBufferSize).not.toHaveBeenCalled();
expect(hci.readBdAddr).not.toHaveBeenCalled();
expect(hci._isExtended).toBe(false);
});
test('should process without extended features', () => {
const cmd = 8195;
const status = 0;
const result = Buffer.from([0x00, 0x00, 0x00, 0x00]); // No bits set
hci.processCmdCompleteEvent(cmd, status, result);
// Verify extended-specific method not called
expect(hci.setCodedPhySupport).not.toHaveBeenCalled();
// Verify other methods were called
expect(hci.setEventMask).toHaveBeenCalled();
expect(hci.setLeEventMask).toHaveBeenCalled();
expect(hci.readLocalVersion).toHaveBeenCalled();
expect(hci.writeLeHostSupported).toHaveBeenCalled();
expect(hci.readLeHostSupported).toHaveBeenCalled();
expect(hci.readLeBufferSize).toHaveBeenCalled();
expect(hci.readBdAddr).toHaveBeenCalled();
expect(hci._isExtended).toBe(false);
});
});
describe('onSocketError', () => {
it('should emit stateChange', () => {
const callback = sinon.spy();
hci.on('stateChange', callback);
hci.onSocketError({ code: 'EPERM', message: 'Network is down' });
assert.calledOnceWithExactly(callback, 'unauthorized');
});
it('should do nothing with message', () => {
const callback = sinon.spy();
hci.on('stateChange', callback);
hci.onSocketError({ message: 'Network is down' });
assert.notCalled(callback);
});
it('should do nothing', () => {
const callback = sinon.spy();
hci.on('stateChange', callback);
hci.onSocketError({ });
assert.notCalled(callback);
});
});
describe('processCmdCompleteEvent', () => {
const aclBuffers = {
length: 99,
num: 88
};
let rssiReadCallback;
let leScanEnableSetCallback;
let leScanParametersSetCallback;
let stateChangeCallback;
let addressChangeCallback;
let readLocalVersionCallback;
beforeEach(() => {
hci.setEventMask = sinon.spy();
hci.setLeEventMask = sinon.spy();
hci.readLocalVersion = sinon.spy();
hci.readBdAddr = sinon.spy();
hci.setScanEnabled = sinon.spy();
hci.setScanParameters = sinon.spy();
hci.readBufferSize = sinon.spy();
hci.setCodedPhySupport = sinon.spy();
hci._aclBuffers = aclBuffers;
rssiReadCallback = sinon.spy();
leScanEnableSetCallback = sinon.spy();
leScanParametersSetCallback = sinon.spy();
stateChangeCallback = sinon.spy();
addressChangeCallback = sinon.spy();
readLocalVersionCallback = sinon.spy();
hci.on('rssiRead', rssiReadCallback);
hci.on('leScanEnableSet', leScanEnableSetCallback);
hci.on('leScanParametersSet', leScanParametersSetCallback);
hci.on('stateChange', stateChangeCallback);
hci.on('addressChange', addressChangeCallback);
hci.on('readLocalVersion', readLocalVersionCallback);
});
it('should do nothing', () => {
const cmd = 0;
const status = 0;
const result = Buffer.from([]);
hci.processCmdCompleteEvent(cmd, status, result);
// called
// not called
assert.notCalled(hci.setEventMask);
assert.notCalled(hci.setLeEventMask);
assert.notCalled(hci.readLocalVersion);
assert.notCalled(hci.readBdAddr);
assert.notCalled(hci.setScanEnabled);
assert.notCalled(hci.setScanParameters);
assert.notCalled(hci.readBufferSize);
assert.notCalled(hci.setCodedPhySupport);
assert.notCalled(rssiReadCallback);
assert.notCalled(leScanEnableSetCallback);
assert.notCalled(leScanParametersSetCallback);
assert.notCalled(stateChangeCallback);
assert.notCalled(addressChangeCallback);
assert.notCalled(readLocalVersionCallback);
// hci checks
should(hci._aclBuffers).deepEqual(aclBuffers);
should(hci._isExtended).equal(false);
});
it('should reset', () => {
const cmd = 3075;
const status = 0;
const result = Buffer.from([]);
hci.processCmdCompleteEvent(cmd, status, result);
// called
assert.calledOnceWithExactly(hci.setEventMask);
assert.calledOnceWithExactly(hci.setLeEventMask);
assert.calledOnceWithExactly(hci.readLocalVersion);
assert.calledOnceWithExactly(hci.readBdAddr);
// not called
assert.notCalled(hci.setScanEnabled);
assert.notCalled(hci.setScanParameters);
assert.notCalled(hci.readBufferSize);
assert.notCalled(hci.setCodedPhySupport);
assert.notCalled(rssiReadCallback);
assert.notCalled(leScanEnableSetCallback);
assert.notCalled(leScanParametersSetCallback);
assert.notCalled(stateChangeCallback);
assert.notCalled(addressChangeCallback);
assert.notCalled(readLocalVersionCallback);
// hci checks
should(hci._aclBuffers).deepEqual(aclBuffers);
should(hci._isExtended).equal(false);
});
it('should reset (extended)', () => {
const cmd = 3075;
const status = 0;
const result = Buffer.from([]);
hci._isExtended = true;
hci.processCmdCompleteEvent(cmd, status, result);
// called
assert.calledOnceWithExactly(hci.setEventMask);
assert.calledOnceWithExactly(hci.setLeEventMask);
assert.calledOnceWithExactly(hci.readLocalVersion);
assert.calledOnceWithExactly(hci.readBdAddr);
assert.calledOnceWithExactly(hci.setCodedPhySupport);
// not called
assert.notCalled(hci.setScanEnabled);
assert.notCalled(hci.setScanParameters);
assert.notCalled(hci.readBufferSize);
assert.notCalled(rssiReadCallback);
assert.notCalled(leScanEnableSetCallback);
assert.notCalled(leScanParametersSetCallback);
assert.notCalled(stateChangeCallback);
assert.notCalled(addressChangeCallback);
assert.notCalled(readLocalVersionCallback);
// hci checks
should(hci._aclBuffers).deepEqual(aclBuffers);
should(hci._isExtended).equal(true);
});
it('should only log debug - READ_LE_HOST_SUPPORTED_CMD', () => {
const cmd = 3180;
const status = 0;
const result = Buffer.from([0, 1]);
hci.processCmdCompleteEvent(cmd, status, result);
// called
// not called
assert.notCalled(hci.setEventMask);
assert.notCalled(hci.setLeEventMask);
assert.notCalled(hci.readLocalVersion);
assert.notCalled(hci.readBdAddr);
assert.notCalled(hci.setScanEnabled);
assert.notCalled(hci.setScanParameters);
assert.notCalled(hci.readBufferSize);
assert.notCalled(hci.setCodedPhySupport);
assert.notCalled(rssiReadCallback);
assert.notCalled(leScanEnableSetCallback);
assert.notCalled(leScanParametersSetCallback);
assert.notCalled(stateChangeCallback);
assert.notCalled(addressChangeCallback);
assert.notCalled(readLocalVersionCallback);
// hci checks
should(hci._aclBuffers).deepEqual(aclBuffers);
should(hci._isExtended).equal(false);
});
it('should do nothing - READ_LE_HOST_SUPPORTED_CMD', () => {
const cmd = 3180;
const status = 1;
const result = Buffer.from([]);
hci.processCmdCompleteEvent(cmd, status, result);
// called
// not called
assert.notCalled(hci.setEventMask);
assert.notCalled(hci.setLeEventMask);
assert.notCalled(hci.readLocalVersion);
assert.notCalled(hci.readBdAddr);
assert.notCalled(hci.setScanEnabled);
assert.notCalled(hci.setScanParameters);
assert.notCalled(hci.readBufferSize);
assert.notCalled(hci.setCodedPhySupport);
assert.notCalled(rssiReadCallback);
assert.notCalled(leScanEnableSetCallback);
assert.notCalled(leScanParametersSetCallback);
assert.notCalled(stateChangeCallback);
assert.notCalled(addressChangeCallback);
assert.notCalled(readLocalVersionCallback);
// hci checks
should(hci._aclBuffers).deepEqual(aclBuffers);
should(hci._isExtended).equal(false);