mirror of
https://github.com/nodejs/node.git
synced 2025-08-15 13:48:44 +02:00

PR-URL: https://github.com/nodejs/node/pull/59311 Refs: https://github.com/nodejs/node/issues/58987 Refs: https://github.com/nodejs/node/issues/59310 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
408 lines
15 KiB
JavaScript
408 lines
15 KiB
JavaScript
'use strict'
|
|
|
|
// Convert `manufacturerData` to an array of bluetooth.BluetoothManufacturerData
|
|
// defined in
|
|
// https://webbluetoothcg.github.io/web-bluetooth/#bluetooth-bidi-definitions.
|
|
function convertToBidiManufacturerData(manufacturerData) {
|
|
const bidiManufacturerData = [];
|
|
for (const key in manufacturerData) {
|
|
bidiManufacturerData.push({
|
|
key: parseInt(key),
|
|
data: btoa(String.fromCharCode(...manufacturerData[key]))
|
|
})
|
|
}
|
|
return bidiManufacturerData;
|
|
}
|
|
|
|
function ArrayToMojoCharacteristicProperties(arr) {
|
|
const struct = {};
|
|
arr.forEach(property => {
|
|
struct[property] = true;
|
|
});
|
|
return struct;
|
|
}
|
|
|
|
class FakeBluetooth {
|
|
constructor() {
|
|
this.fake_central_ = null;
|
|
}
|
|
|
|
// Returns a promise that resolves with a FakeCentral that clients can use
|
|
// to simulate events that a device in the Central/Observer role would
|
|
// receive as well as monitor the operations performed by the device in the
|
|
// Central/Observer role.
|
|
//
|
|
// A "Central" object would allow its clients to receive advertising events
|
|
// and initiate connections to peripherals i.e. operations of two roles
|
|
// defined by the Bluetooth Spec: Observer and Central.
|
|
// See Bluetooth 4.2 Vol 3 Part C 2.2.2 "Roles when Operating over an
|
|
// LE Physical Transport".
|
|
async simulateCentral({state}) {
|
|
if (this.fake_central_) {
|
|
throw 'simulateCentral() should only be called once';
|
|
}
|
|
|
|
await test_driver.bidi.bluetooth.simulate_adapter({state: state});
|
|
this.fake_central_ = new FakeCentral();
|
|
return this.fake_central_;
|
|
}
|
|
}
|
|
|
|
// FakeCentral allows clients to simulate events that a device in the
|
|
// Central/Observer role would receive as well as monitor the operations
|
|
// performed by the device in the Central/Observer role.
|
|
class FakeCentral {
|
|
constructor() {
|
|
this.peripherals_ = new Map();
|
|
}
|
|
|
|
// Simulates a peripheral with |address|, |name|, |manufacturerData| and
|
|
// |known_service_uuids| that has already been connected to the system. If the
|
|
// peripheral existed already it updates its name, manufacturer data, and
|
|
// known UUIDs. |known_service_uuids| should be an array of
|
|
// BluetoothServiceUUIDs
|
|
// https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothserviceuuid
|
|
//
|
|
// Platforms offer methods to retrieve devices that have already been
|
|
// connected to the system or weren't connected through the UA e.g. a user
|
|
// connected a peripheral through the system's settings. This method is
|
|
// intended to simulate peripherals that those methods would return.
|
|
async simulatePreconnectedPeripheral(
|
|
{address, name, manufacturerData = {}, knownServiceUUIDs = []}) {
|
|
await test_driver.bidi.bluetooth.simulate_preconnected_peripheral({
|
|
address: address,
|
|
name: name,
|
|
manufacturerData: convertToBidiManufacturerData(manufacturerData),
|
|
knownServiceUuids:
|
|
knownServiceUUIDs.map(uuid => BluetoothUUID.getService(uuid))
|
|
});
|
|
|
|
return this.fetchOrCreatePeripheral_(address);
|
|
}
|
|
|
|
// Create a fake_peripheral object from the given address.
|
|
fetchOrCreatePeripheral_(address) {
|
|
let peripheral = this.peripherals_.get(address);
|
|
if (peripheral === undefined) {
|
|
peripheral = new FakePeripheral(address);
|
|
this.peripherals_.set(address, peripheral);
|
|
}
|
|
return peripheral;
|
|
}
|
|
}
|
|
|
|
class FakePeripheral {
|
|
constructor(address) {
|
|
this.address = address;
|
|
}
|
|
|
|
// Adds a fake GATT Service with |uuid| to be discovered when discovering
|
|
// the peripheral's GATT Attributes. Returns a FakeRemoteGATTService
|
|
// corresponding to this service. |uuid| should be a BluetoothServiceUUIDs
|
|
// https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothserviceuuid
|
|
async addFakeService({uuid}) {
|
|
const service_uuid = BluetoothUUID.getService(uuid);
|
|
await test_driver.bidi.bluetooth.simulate_service({
|
|
address: this.address,
|
|
uuid: service_uuid,
|
|
type: 'add',
|
|
});
|
|
return new FakeRemoteGATTService(service_uuid, this.address);
|
|
}
|
|
|
|
// Sets the next GATT Connection request response to |code|. |code| could be
|
|
// an HCI Error Code from BT 4.2 Vol 2 Part D 1.3 List Of Error Codes or a
|
|
// number outside that range returned by specific platforms e.g. Android
|
|
// returns 0x101 to signal a GATT failure
|
|
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
|
|
async setNextGATTConnectionResponse({code}) {
|
|
const remove_handler =
|
|
test_driver.bidi.bluetooth.gatt_connection_attempted.on((event) => {
|
|
if (event.address != this.address) {
|
|
return;
|
|
}
|
|
remove_handler();
|
|
test_driver.bidi.bluetooth.simulate_gatt_connection_response({
|
|
address: event.address,
|
|
code,
|
|
});
|
|
});
|
|
}
|
|
|
|
async setNextGATTDiscoveryResponse({code}) {
|
|
// No-op for Web Bluetooth Bidi test, it will be removed when migration
|
|
// completes.
|
|
return Promise.resolve();
|
|
}
|
|
|
|
// Simulates a GATT connection response with |code| from the peripheral.
|
|
async simulateGATTConnectionResponse(code) {
|
|
await test_driver.bidi.bluetooth.simulate_gatt_connection_response(
|
|
{address: this.address, code});
|
|
}
|
|
|
|
// Simulates a GATT disconnection in the peripheral.
|
|
async simulateGATTDisconnection() {
|
|
await test_driver.bidi.bluetooth.simulate_gatt_disconnection(
|
|
{address: this.address});
|
|
}
|
|
}
|
|
|
|
class FakeRemoteGATTService {
|
|
constructor(service_uuid, peripheral_address) {
|
|
this.service_uuid_ = service_uuid;
|
|
this.peripheral_address_ = peripheral_address;
|
|
}
|
|
|
|
// Adds a fake GATT Characteristic with |uuid| and |properties|
|
|
// to this fake service. The characteristic will be found when discovering
|
|
// the peripheral's GATT Attributes. Returns a FakeRemoteGATTCharacteristic
|
|
// corresponding to the added characteristic.
|
|
async addFakeCharacteristic({uuid, properties}) {
|
|
const characteristic_uuid = BluetoothUUID.getCharacteristic(uuid);
|
|
await test_driver.bidi.bluetooth.simulate_characteristic({
|
|
address: this.peripheral_address_,
|
|
serviceUuid: this.service_uuid_,
|
|
characteristicUuid: characteristic_uuid,
|
|
characteristicProperties: ArrayToMojoCharacteristicProperties(properties),
|
|
type: 'add'
|
|
});
|
|
return new FakeRemoteGATTCharacteristic(
|
|
characteristic_uuid, this.service_uuid_, this.peripheral_address_);
|
|
}
|
|
|
|
// Removes the fake GATT service from its fake peripheral.
|
|
async remove() {
|
|
await test_driver.bidi.bluetooth.simulate_service({
|
|
address: this.peripheral_address_,
|
|
uuid: this.service_uuid_,
|
|
type: 'remove'
|
|
});
|
|
}
|
|
}
|
|
|
|
class FakeRemoteGATTCharacteristic {
|
|
constructor(characteristic_uuid, service_uuid, peripheral_address) {
|
|
this.characteristic_uuid_ = characteristic_uuid;
|
|
this.service_uuid_ = service_uuid;
|
|
this.peripheral_address_ = peripheral_address;
|
|
this.last_written_value_ = {lastValue: null, lastWriteType: 'none'};
|
|
}
|
|
|
|
// Adds a fake GATT Descriptor with |uuid| to be discovered when
|
|
// discovering the peripheral's GATT Attributes. Returns a
|
|
// FakeRemoteGATTDescriptor corresponding to this descriptor. |uuid| should
|
|
// be a BluetoothDescriptorUUID
|
|
// https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothdescriptoruuid
|
|
async addFakeDescriptor({uuid}) {
|
|
const descriptor_uuid = BluetoothUUID.getDescriptor(uuid);
|
|
await test_driver.bidi.bluetooth.simulate_descriptor({
|
|
address: this.peripheral_address_,
|
|
serviceUuid: this.service_uuid_,
|
|
characteristicUuid: this.characteristic_uuid_,
|
|
descriptorUuid: descriptor_uuid,
|
|
type: 'add'
|
|
});
|
|
return new FakeRemoteGATTDescriptor(
|
|
descriptor_uuid, this.characteristic_uuid_, this.service_uuid_,
|
|
this.peripheral_address_);
|
|
}
|
|
|
|
// Simulate a characteristic for operation |type| with response |code| and
|
|
// |data|.
|
|
async simulateResponse(type, code, data) {
|
|
await test_driver.bidi.bluetooth.simulate_characteristic_response({
|
|
address: this.peripheral_address_,
|
|
serviceUuid: this.service_uuid_,
|
|
characteristicUuid: this.characteristic_uuid_,
|
|
type,
|
|
code,
|
|
data,
|
|
});
|
|
}
|
|
|
|
// Simulate a characteristic response for read operation with response |code|
|
|
// and |data|.
|
|
async simulateReadResponse(code, data) {
|
|
await this.simulateResponse('read', code, data);
|
|
}
|
|
|
|
// Simulate a characteristic response for write operation with response
|
|
// |code|.
|
|
async simulateWriteResponse(code) {
|
|
await this.simulateResponse('write', code);
|
|
}
|
|
|
|
// Sets the next read response for characteristic to |code| and |value|.
|
|
// |code| could be a GATT Error Response from
|
|
// BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range
|
|
// returned by specific platforms e.g. Android returns 0x101 to signal a GATT
|
|
// failure.
|
|
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
|
|
async setNextReadResponse(gatt_code, value = null) {
|
|
if (gatt_code === 0 && value === null) {
|
|
throw '|value| can\'t be null if read should success.';
|
|
}
|
|
if (gatt_code !== 0 && value !== null) {
|
|
throw '|value| must be null if read should fail.';
|
|
}
|
|
|
|
const remove_handler =
|
|
test_driver.bidi.bluetooth.characteristic_event_generated.on(
|
|
(event) => {
|
|
if (event.address != this.peripheral_address_) {
|
|
return;
|
|
}
|
|
remove_handler();
|
|
this.simulateReadResponse(gatt_code, value);
|
|
});
|
|
}
|
|
|
|
// Sets the next write response for this characteristic to |code|. If
|
|
// writing to a characteristic that only supports 'write-without-response'
|
|
// the set response will be ignored.
|
|
// |code| could be a GATT Error Response from
|
|
// BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range
|
|
// returned by specific platforms e.g. Android returns 0x101 to signal a GATT
|
|
// failure.
|
|
async setNextWriteResponse(gatt_code) {
|
|
const remove_handler =
|
|
test_driver.bidi.bluetooth.characteristic_event_generated.on(
|
|
(event) => {
|
|
if (event.address != this.peripheral_address_) {
|
|
return;
|
|
}
|
|
this.last_written_value_ = {
|
|
lastValue: event.data,
|
|
lastWriteType: event.type
|
|
};
|
|
remove_handler();
|
|
if (event.type == 'write-with-response') {
|
|
this.simulateWriteResponse(gatt_code);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Gets the last successfully written value to the characteristic and its
|
|
// write type. Write type is one of 'none', 'default-deprecated',
|
|
// 'with-response', 'without-response'. Returns {lastValue: null,
|
|
// lastWriteType: 'none'} if no value has yet been written to the
|
|
// characteristic.
|
|
async getLastWrittenValue() {
|
|
return this.last_written_value_;
|
|
}
|
|
|
|
// Removes the fake GATT Characteristic from its fake service.
|
|
async remove() {
|
|
await test_driver.bidi.bluetooth.simulate_characteristic({
|
|
address: this.peripheral_address_,
|
|
serviceUuid: this.service_uuid_,
|
|
characteristicUuid: this.characteristic_uuid_,
|
|
characteristicProperties: undefined,
|
|
type: 'remove'
|
|
});
|
|
}
|
|
}
|
|
|
|
class FakeRemoteGATTDescriptor {
|
|
constructor(
|
|
descriptor_uuid, characteristic_uuid, service_uuid, peripheral_address) {
|
|
this.descriptor_uuid_ = descriptor_uuid;
|
|
this.characteristic_uuid_ = characteristic_uuid;
|
|
this.service_uuid_ = service_uuid;
|
|
this.peripheral_address_ = peripheral_address;
|
|
this.last_written_value_ = null;
|
|
}
|
|
|
|
// Simulate a descriptor for operation |type| with response |code| and
|
|
// |data|.
|
|
async simulateResponse(type, code, data) {
|
|
await test_driver.bidi.bluetooth.simulate_descriptor_response({
|
|
address: this.peripheral_address_,
|
|
serviceUuid: this.service_uuid_,
|
|
characteristicUuid: this.characteristic_uuid_,
|
|
descriptorUuid: this.descriptor_uuid_,
|
|
type,
|
|
code,
|
|
data,
|
|
});
|
|
}
|
|
|
|
// Simulate a descriptor response for read operation with response |code| and
|
|
// |data|.
|
|
async simulateReadResponse(code, data) {
|
|
await this.simulateResponse('read', code, data);
|
|
}
|
|
|
|
// Simulate a descriptor response for write operation with response |code|.
|
|
async simulateWriteResponse(code) {
|
|
await this.simulateResponse('write', code);
|
|
}
|
|
|
|
// Sets the next read response for descriptor to |code| and |value|.
|
|
// |code| could be a GATT Error Response from
|
|
// BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range
|
|
// returned by specific platforms e.g. Android returns 0x101 to signal a GATT
|
|
// failure.
|
|
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
|
|
async setNextReadResponse(gatt_code, value = null) {
|
|
if (gatt_code === 0 && value === null) {
|
|
throw '|value| can\'t be null if read should success.';
|
|
}
|
|
if (gatt_code !== 0 && value !== null) {
|
|
throw '|value| must be null if read should fail.';
|
|
}
|
|
|
|
const remove_handler =
|
|
test_driver.bidi.bluetooth.descriptor_event_generated.on((event) => {
|
|
if (event.address != this.peripheral_address_) {
|
|
return;
|
|
}
|
|
remove_handler();
|
|
this.simulateReadResponse(gatt_code, value);
|
|
});
|
|
}
|
|
|
|
// Sets the next write response for this descriptor to |code|.
|
|
// |code| could be a GATT Error Response from
|
|
// BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range
|
|
// returned by specific platforms e.g. Android returns 0x101 to signal a GATT
|
|
// failure.
|
|
async setNextWriteResponse(gatt_code) {
|
|
const remove_handler =
|
|
test_driver.bidi.bluetooth.descriptor_event_generated.on((event) => {
|
|
if (event.address != this.peripheral_address_) {
|
|
return;
|
|
}
|
|
this.last_written_value_ = {
|
|
lastValue: event.data,
|
|
lastWriteType: event.type
|
|
};
|
|
remove_handler();
|
|
if (event.type == 'write-with-response') {
|
|
this.simulateWriteResponse(gatt_code);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Gets the last successfully written value to the descriptor.
|
|
// Returns null if no value has yet been written to the descriptor.
|
|
async getLastWrittenValue() {
|
|
return this.last_written_value_;
|
|
}
|
|
|
|
// Removes the fake GATT Descriptor from its fake characteristic.
|
|
async remove() {
|
|
await test_driver.bidi.bluetooth.simulate_descriptor({
|
|
address: this.peripheral_address_,
|
|
serviceUuid: this.service_uuid_,
|
|
characteristicUuid: this.characteristic_uuid_,
|
|
descriptorUuid: this.descriptor_uuid_,
|
|
type: 'remove'
|
|
});
|
|
}
|
|
}
|
|
|
|
function initializeBluetoothBidiResources() {
|
|
navigator.bluetooth.test = new FakeBluetooth();
|
|
}
|