Commit 02a8197e authored by Ovidio Henriquez's avatar Ovidio Henriquez Committed by Commit Bot

bluetooth: web: test: Update gc detach test

This change updates the gc-detach.html and detach-gc.html tests to use
promise_test as opposed to async_test. This will help us migrate the
tests to the wpt/ directory, since we use a wrapper around
promise_test to dynamically load Chromium-specific modules.

This change also updates gc-detach.html and detach-gc.html to use
health-thermometer-iframe.html in order to use
getHealthThermometerDevice rather than setBluetoothFakeAdapter. As a
result, health-thermometer-iframe.html now contains a
"RequestAndConnect" message case, and bluetooth-helpers.js does not
rely on the "Ready" message from the health-thermometer-iframe.html
anymore.

Additionally, bluetooth-helpers.js now contains a function called
setUpConnectableHealthThermometerDevice that sets up a
fake_peripheral without requesting the device for tests that require
the device to be requested outside of the main frame.

BUG=769412

Change-Id: I310fd68569fb646e9e4e2ffde202b8f953a47e9b
Reviewed-on: https://chromium-review.googlesource.com/777339
Commit-Queue: Ovidio Henriquez <odejesush@google.com>
Reviewed-by: default avatarGiovanni Ortuño Urquidi <ortuno@chromium.org>
Reviewed-by: default avatarConley Owens <cco3@chromium.org>
Cr-Commit-Position: refs/heads/master@{#521584}
parent 34683f17
...@@ -5,31 +5,32 @@ ...@@ -5,31 +5,32 @@
<script src="../../../resources/testdriver-vendor.js"></script> <script src="../../../resources/testdriver-vendor.js"></script>
<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script> <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
<body> <body>
<script> <script>
"use strict"; 'use strict';
async_test(test => { const test_desc = 'Detach frame then garbage collect. We shouldn\'t crash.';
window.onmessage = messageEvent => test.step(() => { let iframe = document.createElement('iframe');
if (messageEvent.data === 'Ready') {
let iframe = document.querySelector('iframe'); bluetooth_test(() => setUpConnectableHealthThermometerDevice()
callWithTrustedClick(() => { // 1. Load the iframe.
iframe.contentWindow.postMessage('RequestAndConnect', '*'); .then(() => new Promise(resolve => {
}); iframe.src = '../../../resources/bluetooth/health-thermometer-iframe.html';
} else if (messageEvent.data === 'Connected') { document.body.appendChild(iframe);
// Detach iframe.addEventListener('load', resolve);
let iframe = document.querySelector('iframe'); }))
iframe.remove(); // 2. Connect device, detach the iframe, and run garbage collection.
// GC .then(() => new Promise(resolve => {
runGarbageCollection().then(() => test.done()); callWithTrustedClick(() => {
} else { iframe.contentWindow.postMessage({
assert_unreached('iframe sent invalid data: ' + messageEvent.data); type: 'RequestAndConnect',
} options: {filters: [{services: ['health_thermometer']}]}
}, '*');
}); });
setBluetoothFakeAdapter('HeartRateAdapter')
.then(() => { window.onmessage = messageEvent => {
let iframe = document.createElement('iframe'); assert_equals(messageEvent.data, 'Connected');
iframe.src = '../../../resources/bluetooth/heart-rate-iframe.html'; iframe.remove();
document.body.appendChild(iframe); runGarbageCollection().then(resolve);
}); }
}, 'Detach frame then garbage collect. We shouldn\'t crash.'); })), test_desc)
</script> </script>
</body> </body>
...@@ -5,33 +5,34 @@ ...@@ -5,33 +5,34 @@
<script src="../../../resources/testdriver-vendor.js"></script> <script src="../../../resources/testdriver-vendor.js"></script>
<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script> <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
<body> <body>
<script> <script>
"use strict"; 'use strict';
async_test(test => { const test_desc = 'Garbage collect then detach frame. We shouldn\'t crash.';
window.onmessage = messageEvent => test.step(() => { let iframe = document.createElement('iframe');
if (messageEvent.data === 'Ready') {
let iframe = document.querySelector('iframe'); bluetooth_test(() => setUpConnectableHealthThermometerDevice()
callWithTrustedClick(() => { // 1. Load the iframe.
iframe.contentWindow.postMessage('RequestAndConnect', '*'); .then((f) => new Promise(resolve => {
}); iframe.src = '../../../resources/bluetooth/health-thermometer-iframe.html';
} else if (messageEvent.data === 'Connected') { document.body.appendChild(iframe);
let iframe = document.querySelector('iframe'); iframe.addEventListener('load', resolve);
// GC }))
runGarbageCollection().then(() => { // 2. Connect device, run garbage collection, and detach iframe.
// Detach .then(() => new Promise(resolve => {
iframe.remove(); callWithTrustedClick(() => {
test.done(); iframe.contentWindow.postMessage({
}); type: 'RequestAndConnect',
} else { options: {filters: [{services: ['health_thermometer']}]}
assert_unreached('iframe sent invalid data: ' + messageEvent.data); }, '*');
}
}); });
setBluetoothFakeAdapter('HeartRateAdapter')
.then(() => { window.onmessage = messageEvent => {
let iframe = document.createElement('iframe'); assert_equals(messageEvent.data, 'Connected');
iframe.src = '../../../resources/bluetooth/heart-rate-iframe.html'; runGarbageCollection().then(() => {
document.body.appendChild(iframe); iframe.remove();
resolve();
}); });
}, 'Garbage collect then detach frame. We shouldn\'t crash.'); }
</script> })), test_desc)
</script>
</body> </body>
...@@ -337,14 +337,14 @@ function eventPromise(target, type, options) { ...@@ -337,14 +337,14 @@ function eventPromise(target, type, options) {
// of each of event listeners. Otherwise throws an error. // of each of event listeners. Otherwise throws an error.
function assert_promise_event_order_(should_be_first, object, func, event, num_listeners) { function assert_promise_event_order_(should_be_first, object, func, event, num_listeners) {
let order = []; let order = [];
let event_promises = [] let event_promises = [];
for (let i = 0; i < num_listeners; i++) { for (let i = 0; i < num_listeners; i++) {
event_promises.push(new Promise(resolve => { event_promises.push(new Promise(resolve => {
let event_listener = (e) => { let event_listener = (e) => {
object.removeEventListener(event, event_listener); object.removeEventListener(event, event_listener);
order.push('event'); order.push('event');
resolve(e.target.value); resolve(e.target.value);
} };
object.addEventListener(event, event_listener); object.addEventListener(event, event_listener);
})); }));
} }
...@@ -439,7 +439,7 @@ class EventCatcher { ...@@ -439,7 +439,7 @@ class EventCatcher {
let event_listener = () => { let event_listener = () => {
object.removeEventListener(event, event_listener); object.removeEventListener(event, event_listener);
this.eventFired = true; this.eventFired = true;
} };
object.addEventListener(event, event_listener); object.addEventListener(event, event_listener);
} }
} }
...@@ -499,6 +499,17 @@ function setUpPreconnectedDevice({ ...@@ -499,6 +499,17 @@ function setUpPreconnectedDevice({
})); }));
} }
// Returns a FakePeripheral that corresponds to a simulated pre-connected device
// called 'Health Thermometer'. The device has two known serviceUUIDs:
// 'generic_access' and 'health_thermometer'.
function setUpHealthThermometerDevice() {
return setUpPreconnectedDevice({
address: '09:09:09:09:09:09',
name: 'Health Thermometer',
knownServiceUUIDs: ['generic_access', 'health_thermometer'],
});
}
// Returns an array containing two FakePeripherals corresponding // Returns an array containing two FakePeripherals corresponding
// to the simulated devices. // to the simulated devices.
function setUpHealthThermometerAndHeartRateDevices() { function setUpHealthThermometerAndHeartRateDevices() {
...@@ -516,6 +527,18 @@ function setUpHealthThermometerAndHeartRateDevices() { ...@@ -516,6 +527,18 @@ function setUpHealthThermometerAndHeartRateDevices() {
})])); })]));
} }
// Returns the same fake peripheral as setUpHealthThermometerDevice() except
// that connecting to the peripheral will succeed.
function setUpConnectableHealthThermometerDevice() {
let fake_peripheral;
return setUpHealthThermometerDevice()
.then(_ => fake_peripheral = _)
.then(() => fake_peripheral.setNextGATTConnectionResponse({
code: HCI_SUCCESS,
}))
.then(() => fake_peripheral);
}
// Returns an object containing a BluetoothDevice discovered using |options|, // Returns an object containing a BluetoothDevice discovered using |options|,
// its corresponding FakePeripheral and FakeRemoteGATTServices. // its corresponding FakePeripheral and FakeRemoteGATTServices.
// The simulated device is called 'Health Thermometer' it has two known service // The simulated device is called 'Health Thermometer' it has two known service
...@@ -678,39 +701,36 @@ function getConnectedHealthThermometerDevice(options) { ...@@ -678,39 +701,36 @@ function getConnectedHealthThermometerDevice(options) {
// connected to it and discovered its services. // connected to it and discovered its services.
function getHealthThermometerDeviceWithServicesDiscovered(options) { function getHealthThermometerDeviceWithServicesDiscovered(options) {
let device, fake_peripheral, fakes; let device, fake_peripheral, fakes;
return setUpPreconnectedDevice({ let iframe = document.createElement('iframe');
address: '09:09:09:09:09:09', return setUpConnectableHealthThermometerDevice()
name: 'Health Thermometer',
knownServiceUUIDs: ['generic_access', 'health_thermometer'],
})
.then(_ => fake_peripheral = _) .then(_ => fake_peripheral = _)
.then(() => fake_peripheral.setNextGATTConnectionResponse({ .then(() => populateHealthThermometerFakes(fake_peripheral))
code: HCI_SUCCESS, .then(_ => fakes = _)
}))
.then(() => fake_peripheral.setNextGATTDiscoveryResponse({ .then(() => fake_peripheral.setNextGATTDiscoveryResponse({
code: HCI_SUCCESS, code: HCI_SUCCESS,
})) }))
.then(() => populateHealthThermometerFakes(fake_peripheral)) .then(() => new Promise(resolve => {
.then(_ => fakes = _) iframe.src = '../../../resources/bluetooth/health-thermometer-iframe.html';
document.body.appendChild(iframe);
iframe.addEventListener('load', resolve);
}))
.then(() => new Promise((resolve, reject) => { .then(() => new Promise((resolve, reject) => {
let iframe = document.createElement('iframe'); callWithTrustedClick(() => {
iframe.contentWindow.postMessage({
type: 'DiscoverServices',
options: options
}, '*');
});
function messageHandler(messageEvent) { function messageHandler(messageEvent) {
if (messageEvent.data === 'Ready') { if (messageEvent.data == 'DiscoveryComplete') {
callWithTrustedClick(() => iframe.contentWindow.postMessage({
type: 'DiscoverServices',
options: options
}, '*'));
} else if (messageEvent.data === 'DiscoveryComplete') {
window.removeEventListener('message', messageHandler); window.removeEventListener('message', messageHandler);
resolve(); resolve();
} else { } else {
reject(new Error(`Unexpected message: {messageEvent.data}`)); reject(new Error(`Unexpected message: ${messageEvent.data}`));
} }
} }
window.addEventListener('message', messageHandler); window.addEventListener('message', messageHandler);
iframe.src =
'../../../resources/bluetooth/health-thermometer-iframe.html';
document.body.appendChild(iframe);
})) }))
.then(() => requestDeviceWithTrustedClick(options)) .then(() => requestDeviceWithTrustedClick(options))
.then(_ => device = _) .then(_ => device = _)
...@@ -799,18 +819,14 @@ function getHIDDevice(options) { ...@@ -799,18 +819,14 @@ function getHIDDevice(options) {
})); }));
}); });
}); });
}; }
// Similar to getHealthThermometerDevice() except the device // Similar to getHealthThermometerDevice() except the device
// is not connected and thus its services have not been // is not connected and thus its services have not been
// discovered. // discovered.
function getDiscoveredHealthThermometerDevice( function getDiscoveredHealthThermometerDevice(
options = {filters: [{services: ['health_thermometer']}]}) { options = {filters: [{services: ['health_thermometer']}]}) {
return setUpPreconnectedDevice({ return setUpHealthThermometerDevice()
address: '09:09:09:09:09:09',
name: 'Health Thermometer',
knownServiceUUIDs: ['generic_access', 'health_thermometer'],
})
.then(fake_peripheral => { .then(fake_peripheral => {
return requestDeviceWithTrustedClick(options) return requestDeviceWithTrustedClick(options)
.then(device => ({ .then(device => ({
......
<!DOCTYPE html> <!DOCTYPE html>
<script> <script>
let device;
function requestDeviceWithOptionsAndConnect(options) {
return navigator.bluetooth.requestDevice(options)
.then(device => device.gatt.connect());
}
window.onmessage = messageEvent => { window.onmessage = messageEvent => {
if (messageEvent.data.type === 'DiscoverServices') { switch (messageEvent.data.type) {
navigator.bluetooth.requestDevice(messageEvent.data.options) case 'RequestAndConnect':
.then(device => device.gatt.connect()) requestDeviceWithOptionsAndConnect(messageEvent.data.options)
.then(gatt => { .then(gatt => {
return gatt.getPrimaryServices() device = gatt.device;
// TODO(crbug.com/719816): Remove catch once the device parent.postMessage('Connected', '*');
// has some services. }).catch(err => {
.catch(e => { console.error(err);
if (e.name !== 'NotFoundError' || parent.postMessage(`FAIL: ${err}`, '*');
e.message !== 'No Services found in device.') { });
throw e; break;
} case 'DiscoverServices':
}); requestDeviceWithOptionsAndConnect(messageEvent.data.options)
}) .then(gatt => gatt.getPrimaryServices())
.then(() => parent.postMessage('DiscoveryComplete', '*')) .then(() => parent.postMessage('DiscoveryComplete', '*'))
.catch(err => { .catch(err => {
console.error(err); console.error(err);
parent.postMessage('FAIL: ' + err, '*'); parent.postMessage(`FAIL: ${err}`, '*');
}); });
break;
default:
console.error(`Bad message type: ${messageEvent.data.type}`);
parent.postMessage(`FAIL: Bad message type: ${messageEvent.data.type}`,
'*');
} }
}; };
parent.postMessage("Ready", "*");
</script> </script>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment