bluetooth: Migrate some WPT tests to use bidi commands
This CL migrates some Web Bluetooth WPT tests to use WebDriver Bidi bluetooth.simulatePreconnectedPeripheral command. It also creates a new file web-bluetooth-bidi-test.js, which is a counterpart of web-bluetooth-test.js. The following CLs will incrementally implement other necessary functionalities in the file for fully migrating Web Bluetooth WPT tests to run in headless shell. Bug: 41484719 Change-Id: Ia19e8f4b6f637a913f48ae8d3445299ac336d919 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6203507 Commit-Queue: Jack Hsieh <chengweih@chromium.org> Reviewed-by: Matt Reynolds <mattreynolds@chromium.org> Cr-Commit-Position: refs/heads/main@{#1423424}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
db39c7f37d
commit
c4eb35d458
third_party/blink/web_tests
TestLists
external
wpt
bluetooth
bidi
adapter
adapter-absent-getAvailability.https.window.jsadapter-powered-off-getAvailability.https.window.jsadapter-powered-on-getAvailability.https.window.jscross-origin-iframe-getAvailability.sub.https.window.js
requestDevice
resources
resources
@ -80,3 +80,5 @@ virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechRecognition-i
|
||||
virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechRecognition-onerror.https.html
|
||||
virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechRecognition-onstart-onend.https.html
|
||||
virtual/view-transition-mpa-serialization/external/wpt/css/css-view-transitions/window-resize-aborts-transition-before-ready.html
|
||||
# TODO(crbug.com/393152490): Pending Web Bluetooth Permission API integration for running in headless shell.
|
||||
virtual/web-bluetooth-new-permissions-backend/external/wpt/bluetooth/bidi/*
|
||||
|
@ -7,7 +7,7 @@ const test_desc = 'getAvailability() resolves with false if the system does ' +
|
||||
'not have an adapter.';
|
||||
|
||||
bluetooth_bidi_test(async () => {
|
||||
await test_driver.bidi.bluetooth.simulate_adapter({state: "absent"});
|
||||
await navigator.bluetooth.test.simulateCentral({state: 'absent'});
|
||||
let availability = await navigator.bluetooth.getAvailability();
|
||||
assert_false(
|
||||
availability,
|
||||
|
@ -7,7 +7,7 @@ const test_desc = 'getAvailability() resolves with true if the Bluetooth ' +
|
||||
'radio is powered off, but the platform that supports Bluetooth LE.';
|
||||
|
||||
bluetooth_bidi_test(async () => {
|
||||
await test_driver.bidi.bluetooth.simulate_adapter({state: "powered-off"});
|
||||
await navigator.bluetooth.test.simulateCentral({state: 'powered-off'});
|
||||
let availability = await navigator.bluetooth.getAvailability();
|
||||
assert_true(
|
||||
availability,
|
||||
|
@ -7,7 +7,7 @@ const test_desc = 'getAvailability() resolves with true if the Bluetooth ' +
|
||||
'radio is powered on and the platform supports Bluetooth LE.';
|
||||
|
||||
bluetooth_bidi_test(async () => {
|
||||
await test_driver.bidi.bluetooth.simulate_adapter({state: "powered-on"});
|
||||
await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
|
||||
let availability = await navigator.bluetooth.getAvailability();
|
||||
assert_true(
|
||||
availability,
|
||||
|
@ -10,7 +10,7 @@ const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
|
||||
let iframe = document.createElement('iframe');
|
||||
|
||||
bluetooth_bidi_test(async () => {
|
||||
await test_driver.bidi.bluetooth.simulate_adapter({state: "powered-on"});
|
||||
await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
|
||||
await new Promise(resolve => {
|
||||
iframe.src = cross_origin_src;
|
||||
document.body.appendChild(iframe);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// META: script=/resources/testdriver.js
|
||||
// META: script=/resources/testdriver.js?feature=bidi
|
||||
// META: script=/resources/testdriver-vendor.js
|
||||
// META: script=/bluetooth/resources/bluetooth-test.js
|
||||
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
|
||||
@ -6,14 +6,10 @@
|
||||
const test_desc = 'Device with empty name and no UUIDs nearby. Should be ' +
|
||||
'found if acceptAllDevices is true.';
|
||||
|
||||
bluetooth_test(async () => {
|
||||
let { device } = await setUpPreconnectedFakeDevice({
|
||||
fakeDeviceOptions: {
|
||||
name: ''
|
||||
},
|
||||
requestDeviceOptions: {
|
||||
acceptAllDevices: true
|
||||
}
|
||||
bluetooth_bidi_test(async () => {
|
||||
let {device} = await setUpPreconnectedFakeDevice({
|
||||
fakeDeviceOptions: {name: ''},
|
||||
requestDeviceOptions: {acceptAllDevices: true}
|
||||
});
|
||||
assert_equals(device.name, '');
|
||||
}, test_desc);
|
@ -1,4 +1,4 @@
|
||||
// META: script=/resources/testdriver.js
|
||||
// META: script=/resources/testdriver.js?feature=bidi
|
||||
// META: script=/resources/testdriver-vendor.js
|
||||
// META: script=/bluetooth/resources/bluetooth-test.js
|
||||
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
|
||||
@ -7,14 +7,10 @@ const test_desc =
|
||||
'acceptAllDevices is true.';
|
||||
const name = 'LE Device';
|
||||
|
||||
bluetooth_test(async () => {
|
||||
let { device } = await setUpPreconnectedFakeDevice({
|
||||
fakeDeviceOptions: {
|
||||
name: name
|
||||
},
|
||||
requestDeviceOptions: {
|
||||
acceptAllDevices: true
|
||||
}
|
||||
bluetooth_bidi_test(async () => {
|
||||
let {device} = await setUpPreconnectedFakeDevice({
|
||||
fakeDeviceOptions: {name: name},
|
||||
requestDeviceOptions: {acceptAllDevices: true}
|
||||
});
|
||||
assert_equals(device.name, name);
|
||||
}, test_desc);
|
@ -1,4 +1,4 @@
|
||||
// META: script=/resources/testdriver.js
|
||||
// META: script=/resources/testdriver.js?feature=bidi
|
||||
// META: script=/resources/testdriver-vendor.js
|
||||
// META: script=/bluetooth/resources/bluetooth-test.js
|
||||
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
|
||||
@ -7,7 +7,7 @@ const test_desc = 'Reject with NotFoundError if there is no BT radio present.';
|
||||
const expected =
|
||||
new DOMException('Bluetooth adapter not available.', 'NotFoundError');
|
||||
|
||||
bluetooth_test(
|
||||
bluetooth_bidi_test(
|
||||
() => navigator.bluetooth.test.simulateCentral({state: 'absent'})
|
||||
.then(
|
||||
() => assert_promise_rejects_with_message(
|
@ -398,8 +398,11 @@ async function setUpPreconnectedFakeDevice(setupOptionsOverride) {
|
||||
|
||||
// Request the device if the request option isn't empty.
|
||||
if (Object.keys(setupOptions.requestDeviceOptions).length !== 0) {
|
||||
preconnectedDevice.device =
|
||||
await requestDeviceWithTrustedClick(setupOptions.requestDeviceOptions);
|
||||
const prompt_promise = selectFirstDeviceOnDevicePromptUpdated();
|
||||
[preconnectedDevice.device] = await Promise.all([
|
||||
requestDeviceWithTrustedClick(setupOptions.requestDeviceOptions),
|
||||
prompt_promise
|
||||
]);
|
||||
}
|
||||
|
||||
// Set up services discovered state.
|
||||
|
@ -1,5 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
// A flag indicating whether to use Web Bluetooth BiDi commands for Bluetooth
|
||||
// emulation.
|
||||
let useBidi = false;
|
||||
|
||||
/**
|
||||
* Test Setup Helpers
|
||||
*/
|
||||
@ -105,6 +109,15 @@ function bluetooth_bidi_test(
|
||||
test_function, name, properties, validate_response_consumed = true) {
|
||||
return promise_test(async (t) => {
|
||||
assert_implements(navigator.bluetooth, 'missing navigator.bluetooth');
|
||||
|
||||
// Necessary setup for Bluetooth emulation using WebDriver Bidi commands.
|
||||
useBidi = true;
|
||||
await loadScript('/resources/web-bluetooth-bidi-test.js');
|
||||
await initializeBluetoothBidiResources();
|
||||
assert_implements(
|
||||
navigator.bluetooth.test, 'missing navigator.bluetooth.test');
|
||||
await test_driver.bidi.bluetooth.request_device_prompt_updated.subscribe();
|
||||
|
||||
await test_function(t);
|
||||
}, name, properties);
|
||||
}
|
||||
@ -153,6 +166,28 @@ async function callWithTrustedClick(callback) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a one-time handler that selects the first device in the device
|
||||
* prompt upon a device prompt updated event.
|
||||
* @returns {Promise<void>} Fulfilled after the Bluetooth device prompt
|
||||
* is handled, or rejected if the operation fails.
|
||||
*/
|
||||
function selectFirstDeviceOnDevicePromptUpdated() {
|
||||
if (!useBidi) {
|
||||
// Return a resolved promise when there is no bidi support.
|
||||
return Promise.resolve();
|
||||
}
|
||||
test_driver.bidi.bluetooth.request_device_prompt_updated.once().then(
|
||||
(promptEvent) => {
|
||||
assert_greater_than_equal(promptEvent.devices.length, 0);
|
||||
return test_driver.bidi.bluetooth.handle_request_device_prompt({
|
||||
prompt: promptEvent.prompt,
|
||||
accept: true,
|
||||
device: promptEvent.devices[0].id
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls requestDevice() in a context that's 'allowed to show a popup'.
|
||||
* @returns {Promise<BluetoothDevice>} Resolves with a Bluetooth device if
|
||||
|
91
third_party/blink/web_tests/external/wpt/resources/web-bluetooth-bidi-test.js
vendored
Normal file
91
third_party/blink/web_tests/external/wpt/resources/web-bluetooth-bidi-test.js
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
'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(manufacturerData[key].buffer)})
|
||||
}
|
||||
return bidiManufacturerData;
|
||||
}
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
function initializeBluetoothBidiResources() {
|
||||
navigator.bluetooth.test = new FakeBluetooth();
|
||||
}
|
Reference in New Issue
Block a user