Skip to content

Commit cc2a485

Browse files
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 <[email protected]> Reviewed-by: Matt Reynolds <[email protected]> Cr-Commit-Position: refs/heads/main@{#1423424}
1 parent f3f7fed commit cc2a485

10 files changed

+147
-26
lines changed

Diff for: bluetooth/bidi/adapter/adapter-absent-getAvailability.https.window.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const test_desc = 'getAvailability() resolves with false if the system does ' +
77
'not have an adapter.';
88

99
bluetooth_bidi_test(async () => {
10-
await test_driver.bidi.bluetooth.simulate_adapter({state: "absent"});
10+
await navigator.bluetooth.test.simulateCentral({state: 'absent'});
1111
let availability = await navigator.bluetooth.getAvailability();
1212
assert_false(
1313
availability,

Diff for: bluetooth/bidi/adapter/adapter-powered-off-getAvailability.https.window.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const test_desc = 'getAvailability() resolves with true if the Bluetooth ' +
77
'radio is powered off, but the platform that supports Bluetooth LE.';
88

99
bluetooth_bidi_test(async () => {
10-
await test_driver.bidi.bluetooth.simulate_adapter({state: "powered-off"});
10+
await navigator.bluetooth.test.simulateCentral({state: 'powered-off'});
1111
let availability = await navigator.bluetooth.getAvailability();
1212
assert_true(
1313
availability,

Diff for: bluetooth/bidi/adapter/adapter-powered-on-getAvailability.https.window.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const test_desc = 'getAvailability() resolves with true if the Bluetooth ' +
77
'radio is powered on and the platform supports Bluetooth LE.';
88

99
bluetooth_bidi_test(async () => {
10-
await test_driver.bidi.bluetooth.simulate_adapter({state: "powered-on"});
10+
await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
1111
let availability = await navigator.bluetooth.getAvailability();
1212
assert_true(
1313
availability,

Diff for: bluetooth/bidi/adapter/cross-origin-iframe-getAvailability.sub.https.window.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
1010
let iframe = document.createElement('iframe');
1111

1212
bluetooth_bidi_test(async () => {
13-
await test_driver.bidi.bluetooth.simulate_adapter({state: "powered-on"});
13+
await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
1414
await new Promise(resolve => {
1515
iframe.src = cross_origin_src;
1616
document.body.appendChild(iframe);
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
1-
// META: script=/resources/testdriver.js
1+
// META: script=/resources/testdriver.js?feature=bidi
22
// META: script=/resources/testdriver-vendor.js
33
// META: script=/bluetooth/resources/bluetooth-test.js
44
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
55
'use strict';
66
const test_desc = 'Device with empty name and no UUIDs nearby. Should be ' +
77
'found if acceptAllDevices is true.';
88

9-
bluetooth_test(async () => {
10-
let { device } = await setUpPreconnectedFakeDevice({
11-
fakeDeviceOptions: {
12-
name: ''
13-
},
14-
requestDeviceOptions: {
15-
acceptAllDevices: true
16-
}
9+
bluetooth_bidi_test(async () => {
10+
let {device} = await setUpPreconnectedFakeDevice({
11+
fakeDeviceOptions: {name: ''},
12+
requestDeviceOptions: {acceptAllDevices: true}
1713
});
1814
assert_equals(device.name, '');
1915
}, test_desc);
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// META: script=/resources/testdriver.js
1+
// META: script=/resources/testdriver.js?feature=bidi
22
// META: script=/resources/testdriver-vendor.js
33
// META: script=/bluetooth/resources/bluetooth-test.js
44
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
@@ -7,14 +7,10 @@ const test_desc =
77
'acceptAllDevices is true.';
88
const name = 'LE Device';
99

10-
bluetooth_test(async () => {
11-
let { device } = await setUpPreconnectedFakeDevice({
12-
fakeDeviceOptions: {
13-
name: name
14-
},
15-
requestDeviceOptions: {
16-
acceptAllDevices: true
17-
}
10+
bluetooth_bidi_test(async () => {
11+
let {device} = await setUpPreconnectedFakeDevice({
12+
fakeDeviceOptions: {name: name},
13+
requestDeviceOptions: {acceptAllDevices: true}
1814
});
1915
assert_equals(device.name, name);
2016
}, test_desc);

Diff for: bluetooth/legacy/requestDevice/radio-not-present.https.window.js renamed to bluetooth/bidi/requestDevice/radio-not-present.https.window.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// META: script=/resources/testdriver.js
1+
// META: script=/resources/testdriver.js?feature=bidi
22
// META: script=/resources/testdriver-vendor.js
33
// META: script=/bluetooth/resources/bluetooth-test.js
44
// 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.';
77
const expected =
88
new DOMException('Bluetooth adapter not available.', 'NotFoundError');
99

10-
bluetooth_test(
10+
bluetooth_bidi_test(
1111
() => navigator.bluetooth.test.simulateCentral({state: 'absent'})
1212
.then(
1313
() => assert_promise_rejects_with_message(

Diff for: bluetooth/resources/bluetooth-fake-devices.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,11 @@ async function setUpPreconnectedFakeDevice(setupOptionsOverride) {
398398

399399
// Request the device if the request option isn't empty.
400400
if (Object.keys(setupOptions.requestDeviceOptions).length !== 0) {
401-
preconnectedDevice.device =
402-
await requestDeviceWithTrustedClick(setupOptions.requestDeviceOptions);
401+
const prompt_promise = selectFirstDeviceOnDevicePromptUpdated();
402+
[preconnectedDevice.device] = await Promise.all([
403+
requestDeviceWithTrustedClick(setupOptions.requestDeviceOptions),
404+
prompt_promise
405+
]);
403406
}
404407

405408
// Set up services discovered state.

Diff for: bluetooth/resources/bluetooth-test.js

+35
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
'use strict';
22

3+
// A flag indicating whether to use Web Bluetooth BiDi commands for Bluetooth
4+
// emulation.
5+
let useBidi = false;
6+
37
/**
48
* Test Setup Helpers
59
*/
@@ -105,6 +109,15 @@ function bluetooth_bidi_test(
105109
test_function, name, properties, validate_response_consumed = true) {
106110
return promise_test(async (t) => {
107111
assert_implements(navigator.bluetooth, 'missing navigator.bluetooth');
112+
113+
// Necessary setup for Bluetooth emulation using WebDriver Bidi commands.
114+
useBidi = true;
115+
await loadScript('/resources/web-bluetooth-bidi-test.js');
116+
await initializeBluetoothBidiResources();
117+
assert_implements(
118+
navigator.bluetooth.test, 'missing navigator.bluetooth.test');
119+
await test_driver.bidi.bluetooth.request_device_prompt_updated.subscribe();
120+
108121
await test_function(t);
109122
}, name, properties);
110123
}
@@ -153,6 +166,28 @@ async function callWithTrustedClick(callback) {
153166
});
154167
}
155168

169+
/**
170+
* Registers a one-time handler that selects the first device in the device
171+
* prompt upon a device prompt updated event.
172+
* @returns {Promise<void>} Fulfilled after the Bluetooth device prompt
173+
* is handled, or rejected if the operation fails.
174+
*/
175+
function selectFirstDeviceOnDevicePromptUpdated() {
176+
if (!useBidi) {
177+
// Return a resolved promise when there is no bidi support.
178+
return Promise.resolve();
179+
}
180+
test_driver.bidi.bluetooth.request_device_prompt_updated.once().then(
181+
(promptEvent) => {
182+
assert_greater_than_equal(promptEvent.devices.length, 0);
183+
return test_driver.bidi.bluetooth.handle_request_device_prompt({
184+
prompt: promptEvent.prompt,
185+
accept: true,
186+
device: promptEvent.devices[0].id
187+
});
188+
});
189+
}
190+
156191
/**
157192
* Calls requestDevice() in a context that's 'allowed to show a popup'.
158193
* @returns {Promise<BluetoothDevice>} Resolves with a Bluetooth device if

Diff for: resources/web-bluetooth-bidi-test.js

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
'use strict'
2+
3+
// Convert `manufacturerData` to an array of bluetooth.BluetoothManufacturerData
4+
// defined in
5+
// https://webbluetoothcg.github.io/web-bluetooth/#bluetooth-bidi-definitions.
6+
function convertToBidiManufacturerData(manufacturerData) {
7+
const bidiManufacturerData = [];
8+
for (const key in manufacturerData) {
9+
bidiManufacturerData.push(
10+
{key: parseInt(key), data: btoa(manufacturerData[key].buffer)})
11+
}
12+
return bidiManufacturerData;
13+
}
14+
15+
class FakeBluetooth {
16+
constructor() {
17+
this.fake_central_ = null;
18+
}
19+
20+
// Returns a promise that resolves with a FakeCentral that clients can use
21+
// to simulate events that a device in the Central/Observer role would
22+
// receive as well as monitor the operations performed by the device in the
23+
// Central/Observer role.
24+
//
25+
// A "Central" object would allow its clients to receive advertising events
26+
// and initiate connections to peripherals i.e. operations of two roles
27+
// defined by the Bluetooth Spec: Observer and Central.
28+
// See Bluetooth 4.2 Vol 3 Part C 2.2.2 "Roles when Operating over an
29+
// LE Physical Transport".
30+
async simulateCentral({state}) {
31+
if (this.fake_central_) {
32+
throw 'simulateCentral() should only be called once';
33+
}
34+
35+
await test_driver.bidi.bluetooth.simulate_adapter({state: state});
36+
this.fake_central_ = new FakeCentral();
37+
return this.fake_central_;
38+
}
39+
}
40+
41+
// FakeCentral allows clients to simulate events that a device in the
42+
// Central/Observer role would receive as well as monitor the operations
43+
// performed by the device in the Central/Observer role.
44+
class FakeCentral {
45+
constructor() {
46+
this.peripherals_ = new Map();
47+
}
48+
49+
// Simulates a peripheral with |address|, |name|, |manufacturerData| and
50+
// |known_service_uuids| that has already been connected to the system. If the
51+
// peripheral existed already it updates its name, manufacturer data, and
52+
// known UUIDs. |known_service_uuids| should be an array of
53+
// BluetoothServiceUUIDs
54+
// https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothserviceuuid
55+
//
56+
// Platforms offer methods to retrieve devices that have already been
57+
// connected to the system or weren't connected through the UA e.g. a user
58+
// connected a peripheral through the system's settings. This method is
59+
// intended to simulate peripherals that those methods would return.
60+
async simulatePreconnectedPeripheral(
61+
{address, name, manufacturerData = {}, knownServiceUUIDs = []}) {
62+
await test_driver.bidi.bluetooth.simulate_preconnected_peripheral({
63+
address: address,
64+
name: name,
65+
manufacturerData: convertToBidiManufacturerData(manufacturerData),
66+
knownServiceUuids: knownServiceUUIDs
67+
});
68+
69+
return this.fetchOrCreatePeripheral_(address);
70+
}
71+
72+
// Create a fake_peripheral object from the given address.
73+
fetchOrCreatePeripheral_(address) {
74+
let peripheral = this.peripherals_.get(address);
75+
if (peripheral === undefined) {
76+
peripheral = new FakePeripheral(address);
77+
this.peripherals_.set(address, peripheral);
78+
}
79+
return peripheral;
80+
}
81+
}
82+
83+
class FakePeripheral {
84+
constructor(address) {
85+
this.address = address;
86+
}
87+
}
88+
89+
function initializeBluetoothBidiResources() {
90+
navigator.bluetooth.test = new FakeBluetooth();
91+
}

0 commit comments

Comments
 (0)