summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/bluetooth/server
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/bluetooth/server
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/bluetooth/server')
-rw-r--r--testing/web-platform/tests/bluetooth/server/connect/connection-succeeds.https.window.js13
-rw-r--r--testing/web-platform/tests/bluetooth/server/connect/detachedIframe.https.window.js27
-rw-r--r--testing/web-platform/tests/bluetooth/server/connect/garbage-collection-ran-during-success.https.window.js19
-rw-r--r--testing/web-platform/tests/bluetooth/server/connect/get-same-gatt-server.https.window.js19
-rw-r--r--testing/web-platform/tests/bluetooth/server/device-same-object.https.window.js13
-rw-r--r--testing/web-platform/tests/bluetooth/server/disconnect/connect-disconnect-twice.https.window.js23
-rw-r--r--testing/web-platform/tests/bluetooth/server/disconnect/detach-gc.https.window.js34
-rw-r--r--testing/web-platform/tests/bluetooth/server/disconnect/detachedIframe.https.window.js28
-rw-r--r--testing/web-platform/tests/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.window.js20
-rw-r--r--testing/web-platform/tests/bluetooth/server/disconnect/gc-detach.https.window.js36
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-called-before.https.window.js26
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.https.window.js25
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.https.window.js26
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-discovery-timeout.https.window.js46
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.https.window.js43
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnected-device.https.window.js23
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-discovery-complete-no-permission-absent-service.https.window.js29
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-discovery-complete-service-not-found.https.window.js20
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-error.https.window.js28
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.https.window.js28
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.https.window.js39
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-get-same-object.https.window.js37
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-invalid-service-name.https.window.js26
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-no-permission-absent-service.https.window.js27
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.https.window.js21
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-no-permission-present-service.https.window.js26
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-service-not-found.https.window.js20
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/service-found.https.window.js28
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.window.js88
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.window.js20
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/blocklisted-services.https.window.js22
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/correct-services.https.window.js30
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.https.window.js26
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before.https.window.js26
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.https.window.js25
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error.https.window.js25
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success-with-uuid.https.window.js26
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success.https.window.js26
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-discovery-timeout-with-uuid.https.window.js46
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-discovery-timeout.https.window.js46
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.https.window.js43
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.https.window.js43
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnected-device-with-uuid.https.window.js23
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnected-device.https.window.js23
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-discovery-complete-no-permission-absent-service-with-uuid.https.window.js29
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-discovery-complete-service-not-found-with-uuid.https.window.js20
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.https.window.js28
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error.https.window.js28
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.https.window.js28
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.https.window.js28
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.https.window.js39
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.https.window.js39
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.https.window.js37
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-same-object.https.window.js37
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.https.window.js26
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-absent-service-with-uuid.https.window.js27
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.https.window.js21
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.https.window.js21
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.https.window.js26
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.https.window.js20
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.window.js25
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/services-found.https.window.js25
-rw-r--r--testing/web-platform/tests/bluetooth/server/getPrimaryServices/services-not-found.https.window.js15
63 files changed, 1807 insertions, 0 deletions
diff --git a/testing/web-platform/tests/bluetooth/server/connect/connection-succeeds.https.window.js b/testing/web-platform/tests/bluetooth/server/connect/connection-succeeds.https.window.js
new file mode 100644
index 0000000000..90b62b9265
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/connect/connection-succeeds.https.window.js
@@ -0,0 +1,13 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Device will connect';
+
+bluetooth_test(async () => {
+ let {device, fake_peripheral} = await getDiscoveredHealthThermometerDevice();
+ await fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS});
+ let gatt = await device.gatt.connect();
+ assert_true(gatt.connected);
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/server/connect/detachedIframe.https.window.js b/testing/web-platform/tests/bluetooth/server/connect/detachedIframe.https.window.js
new file mode 100644
index 0000000000..2332cef707
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/connect/detachedIframe.https.window.js
@@ -0,0 +1,27 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+
+bluetooth_test(async () => {
+ let iframe = document.createElement('iframe');
+ let error;
+
+ const {device} = await getHealthThermometerDeviceFromIframe(iframe);
+
+ iframe.remove();
+ // Set iframe to null to ensure that the GC cleans up as much as possible.
+ iframe = null;
+ await garbageCollect();
+
+ try {
+ await device.gatt.connect();
+ } catch (e) {
+ // Cannot use promise_rejects_dom() because |e| is thrown from a different
+ // global.
+ error = e;
+ }
+ assert_not_equals(error, undefined);
+ assert_equals(error.name, 'NetworkError');
+}, 'connect() rejects in a detached context');
diff --git a/testing/web-platform/tests/bluetooth/server/connect/garbage-collection-ran-during-success.https.window.js b/testing/web-platform/tests/bluetooth/server/connect/garbage-collection-ran-during-success.https.window.js
new file mode 100644
index 0000000000..2d2211dec3
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/connect/garbage-collection-ran-during-success.https.window.js
@@ -0,0 +1,19 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Garbage Collection ran during a connect call that ' +
+ 'succeeds. Should not crash.';
+
+bluetooth_test(async () => {
+ let connectPromise;
+ {
+ let {device, fake_peripheral} =
+ await getDiscoveredHealthThermometerDevice();
+ await fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS});
+ connectPromise = device.gatt.connect();
+ }
+ await Promise.all([connectPromise, garbageCollect()]);
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/server/connect/get-same-gatt-server.https.window.js b/testing/web-platform/tests/bluetooth/server/connect/get-same-gatt-server.https.window.js
new file mode 100644
index 0000000000..59d7243a65
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/connect/get-same-gatt-server.https.window.js
@@ -0,0 +1,19 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Multiple connects should return the same gatt object.';
+
+bluetooth_test(async () => {
+ let {device, fake_peripheral} = await getDiscoveredHealthThermometerDevice();
+ await fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS});
+ // No second response is necessary because an ATT Bearer
+ // already exists from the first connection.
+ // See
+ // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect
+ // step 5.1.
+ let gatt1 = await device.gatt.connect();
+ let gatt2 = await device.gatt.connect();
+ assert_equals(gatt1, gatt2);
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/server/device-same-object.https.window.js b/testing/web-platform/tests/bluetooth/server/device-same-object.https.window.js
new file mode 100644
index 0000000000..f9a66d9b69
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/device-same-object.https.window.js
@@ -0,0 +1,13 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = '[SameObject] test for BluetoothRemoteGATTServer\'s device.';
+
+bluetooth_test(async () => {
+ let {device, fake_peripheral} = await getDiscoveredHealthThermometerDevice();
+ await fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS});
+ let gatt = await device.gatt.connect();
+ assert_equals(gatt.device, device);
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/server/disconnect/connect-disconnect-twice.https.window.js b/testing/web-platform/tests/bluetooth/server/disconnect/connect-disconnect-twice.https.window.js
new file mode 100644
index 0000000000..5d9908df4c
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/disconnect/connect-disconnect-twice.https.window.js
@@ -0,0 +1,23 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Connect + Disconnect twice still results in ' +
+ '\'connected\' being false.';
+let device, fake_peripheral;
+
+// TODO(569716): Test that the disconnect signal was sent to the device.
+bluetooth_test(async () => {
+ let {device, fake_peripheral} = await getDiscoveredHealthThermometerDevice();
+ await fake_peripheral.setNextGATTConnectionResponse({
+ code: HCI_SUCCESS,
+ });
+ let gattServer = await device.gatt.connect();
+ await gattServer.disconnect();
+ assert_false(gattServer.connected);
+
+ gattServer = await device.gatt.connect();
+ await gattServer.disconnect();
+ assert_false(gattServer.connected);
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/server/disconnect/detach-gc.https.window.js b/testing/web-platform/tests/bluetooth/server/disconnect/detach-gc.https.window.js
new file mode 100644
index 0000000000..b934b37973
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/disconnect/detach-gc.https.window.js
@@ -0,0 +1,34 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Detach frame then garbage collect. We shouldn\'t crash.';
+let iframe = document.createElement('iframe');
+
+bluetooth_test(async () => {
+ await setUpConnectableHealthThermometerDevice();
+ // 1. Load the iframe.
+ await new Promise(resolve => {
+ iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
+ document.body.appendChild(iframe);
+ iframe.addEventListener('load', resolve);
+ });
+ // 2. Connect device, detach the iframe, and run garbage collection.
+ await new Promise(resolve => {
+ callWithTrustedClick(() => {
+ iframe.contentWindow.postMessage(
+ {
+ type: 'RequestAndConnect',
+ options: {filters: [{services: ['health_thermometer']}]}
+ },
+ '*');
+ });
+ window.onmessage = messageEvent => {
+ assert_equals(messageEvent.data, 'Connected');
+ iframe.remove();
+ garbageCollect().then(resolve);
+ }
+ })
+}, test_desc)
diff --git a/testing/web-platform/tests/bluetooth/server/disconnect/detachedIframe.https.window.js b/testing/web-platform/tests/bluetooth/server/disconnect/detachedIframe.https.window.js
new file mode 100644
index 0000000000..04e0ca0117
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/disconnect/detachedIframe.https.window.js
@@ -0,0 +1,28 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+
+bluetooth_test(async () => {
+ let iframe = document.createElement('iframe');
+ let error;
+
+ const {device} = await getHealthThermometerDeviceFromIframe(iframe);
+ await device.gatt.connect();
+
+ iframe.remove();
+ // Set iframe to null to ensure that the GC cleans up as much as possible.
+ iframe = null;
+ await garbageCollect();
+
+ try {
+ await device.gatt.disconnect();
+ } catch (e) {
+ // Cannot use promise_rejects_dom() because |e| is thrown from a different
+ // global.
+ error = e;
+ }
+ assert_not_equals(error, undefined);
+ assert_equals(error.name, 'NetworkError');
+}, 'disconnect() rejects in a detached context');
diff --git a/testing/web-platform/tests/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.window.js b/testing/web-platform/tests/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.window.js
new file mode 100644
index 0000000000..acca9796d5
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/disconnect/disconnect-twice-in-a-row.https.window.js
@@ -0,0 +1,20 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Calling disconnect twice in a row still results in ' +
+ '\'connected\' being false.';
+
+// TODO(569716): Test that the disconnect signal was sent to the device.
+bluetooth_test(async () => {
+ let {device, fake_peripheral} = await getDiscoveredHealthThermometerDevice();
+ await fake_peripheral.setNextGATTConnectionResponse({
+ code: HCI_SUCCESS,
+ });
+ let gattServer = await device.gatt.connect();
+ await gattServer.disconnect();
+ assert_false(gattServer.connected);
+ await gattServer.disconnect();
+ assert_false(gattServer.connected);
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/server/disconnect/gc-detach.https.window.js b/testing/web-platform/tests/bluetooth/server/disconnect/gc-detach.https.window.js
new file mode 100644
index 0000000000..1c062a7759
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/disconnect/gc-detach.https.window.js
@@ -0,0 +1,36 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Garbage collect then detach frame. We shouldn\'t crash.';
+let iframe = document.createElement('iframe');
+
+bluetooth_test(async () => {
+ await setUpConnectableHealthThermometerDevice();
+ // 1. Load the iframe.
+ await new Promise(resolve => {
+ iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
+ document.body.appendChild(iframe);
+ iframe.addEventListener('load', resolve);
+ });
+ // 2. Connect device, run garbage collection, and detach iframe.
+ await new Promise(resolve => {
+ callWithTrustedClick(() => {
+ iframe.contentWindow.postMessage(
+ {
+ type: 'RequestAndConnect',
+ options: {filters: [{services: ['health_thermometer']}]}
+ },
+ '*');
+ });
+ window.onmessage = messageEvent => {
+ assert_equals(messageEvent.data, 'Connected');
+ garbageCollect().then(() => {
+ iframe.remove();
+ resolve();
+ });
+ }
+ })
+}, test_desc)
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-called-before.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-called-before.https.window.js
new file mode 100644
index 0000000000..631545a385
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-called-before.https.window.js
@@ -0,0 +1,26 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'disconnect() called before getPrimaryService. ' +
+ 'Reject with NetworkError.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect ' +
+ 'first with `device.gatt.connect`.',
+ 'NetworkError');
+let device;
+
+bluetooth_test(() => getConnectedHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ })
+ .then(_ => ({device} = _))
+ .then(() => device.gatt.disconnect())
+ .then(() => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService('health_thermometer'),
+ expected)),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.https.window.js
new file mode 100644
index 0000000000..bcf19665d5
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.https.window.js
@@ -0,0 +1,25 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'disconnect() called during a getPrimaryService ' +
+ 'call that fails. Reject with NetworkError.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect ' +
+ 'first with `device.gatt.connect`.', 'NetworkError');
+let device;
+
+bluetooth_test(() => getEmptyHealthThermometerDevice()
+ .then(_ => ({device} = _))
+ .then(() => {
+ let promise = assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService('health_thermometer'),
+ expected)
+ device.gatt.disconnect();
+ return promise;
+ }),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.https.window.js
new file mode 100644
index 0000000000..0d2fc1044a
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.https.window.js
@@ -0,0 +1,26 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'disconnect() called during a getPrimaryService call that ' +
+ 'succeeds. Reject with NetworkError.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect ' +
+ 'first with `device.gatt.connect`.',
+ 'NetworkError');
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ })
+ .then(({device}) => {
+ let promise = assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService('health_thermometer'),
+ expected);
+ device.gatt.disconnect();
+ return promise;
+ }), test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-discovery-timeout.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-discovery-timeout.https.window.js
new file mode 100644
index 0000000000..03b0c9d0f3
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-discovery-timeout.https.window.js
@@ -0,0 +1,46 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc =
+ 'Calls to getPrimaryService when device disconnects and discovery' +
+ ' times out should reject promise rather than get stuck.';
+let device;
+
+bluetooth_test(
+ async (t) => {
+ let {device, fake_peripheral} =
+ await getConnectedHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ });
+
+ await fake_peripheral.setNextGATTDiscoveryResponse({
+ code: HCI_CONNECTION_TIMEOUT,
+ });
+ await Promise.all([
+ fake_peripheral.simulateGATTDisconnection({
+ code: HCI_SUCCESS,
+ }),
+ // Using promise_rejects_dom here rather than
+ // assert_promise_rejects_with_message as the race between
+ // simulateGATTDisconnection and getPrimaryServices might end up giving
+ // slightly different exception message (i.e has "Failed to execute ...
+ // on
+ // ... " prefix when disconnected state is reflected on the renderer
+ // side). The point of the test is no matter how race between them, the
+ // promise will be rejected as opposed to get stuck.
+ promise_rejects_dom(t, 'NetworkError', device.gatt.getPrimaryService('health_thermometer')),
+ ]);
+ },
+ test_desc, '',
+ // As specified above there is a race condition between
+ // simulateGATTDisconnection and getPrimaryServices, the artificial
+ // GATTDiscoveryResponse might not be consumed in case
+ // simulateGATTDisconnection happens first. As a result explicitly skip
+ // all response consumed validation at the end of the test.
+ /*validate_response_consumed=*/ false);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.https.window.js
new file mode 100644
index 0000000000..56468b24ea
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.https.window.js
@@ -0,0 +1,43 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Calls on services after we disconnect and connect again. '+
+ 'Should reject with InvalidStateError.';
+let device, services;
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}]
+ })
+ .then(_ => ({device} = _))
+ .then(() => device.gatt.getPrimaryService('health_thermometer'))
+ // Convert to array if necessary.
+ .then(s => services = [].concat(s))
+ .then(() => device.gatt.disconnect())
+ .then(() => device.gatt.connect())
+ .then(() => {
+ let promises = Promise.resolve();
+ for (let service of services) {
+ let error = new DOMException(
+ `Service with UUID ${service.uuid} is no longer valid. Remember ` +
+ `to retrieve the service again after reconnecting.`,
+ 'InvalidStateError');
+ promises = promises.then(() =>
+ assert_promise_rejects_with_message(
+ service.getCharacteristic('measurement_interval'),
+ error));
+ promises = promises.then(() =>
+ assert_promise_rejects_with_message(
+ service.getCharacteristics(),
+ error));
+ promises = promises.then(() =>
+ assert_promise_rejects_with_message(
+ service.getCharacteristics('measurement_interval'),
+ error));
+ }
+ return promises;
+ }), test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnected-device.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnected-device.https.window.js
new file mode 100644
index 0000000000..741b2db5ee
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-disconnected-device.https.window.js
@@ -0,0 +1,23 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'getPrimaryService called before connecting. Reject with ' +
+ 'NetworkError.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect ' +
+ 'first with `device.gatt.connect`.',
+ 'NetworkError');
+
+bluetooth_test(() => getDiscoveredHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ })
+ .then(({device}) => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService('health_thermometer'),
+ expected)),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-discovery-complete-no-permission-absent-service.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-discovery-complete-no-permission-absent-service.https.window.js
new file mode 100644
index 0000000000..e2f5c87630
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-discovery-complete-no-permission-absent-service.https.window.js
@@ -0,0 +1,29 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Request for absent service without permission. Should ' +
+ 'Reject with SecurityError even if services have been discovered already.';
+const expected = new DOMException(
+ 'Origin is not allowed to access the service. Tip: Add the service ' +
+ 'UUID to \'optionalServices\' in requestDevice() options. ' +
+ 'https://goo.gl/HxfxSQ',
+ 'SecurityError');
+let device;
+
+bluetooth_test(() => getHealthThermometerDeviceWithServicesDiscovered({
+ filters: [{services: ['health_thermometer']}]
+ })
+ .then(_ => ({device} = _))
+ .then(() => Promise.all([
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService(glucose.alias), expected),
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService(glucose.name), expected),
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService(glucose.uuid), expected)])),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-discovery-complete-service-not-found.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-discovery-complete-service-not-found.https.window.js
new file mode 100644
index 0000000000..8e9166b41a
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-discovery-complete-service-not-found.https.window.js
@@ -0,0 +1,20 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Request for absent service. Must reject with ' +
+ 'NotFoundError even when the services have previously been discovered.';
+
+bluetooth_test(() => getHealthThermometerDeviceWithServicesDiscovered({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['glucose']})
+ .then(({device}) => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService('glucose'),
+ new DOMException(
+ `No Services matching UUID ${glucose.uuid} found in Device.`,
+ 'NotFoundError'))),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-error.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-error.https.window.js
new file mode 100644
index 0000000000..df182fe8ff
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-error.https.window.js
@@ -0,0 +1,28 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Garbage Collection ran during a getPrimaryService ' +
+ 'call that failed. Should not crash.'
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect first ' +
+ 'with `device.gatt.connect`.',
+ 'NetworkError');
+let promise;
+
+bluetooth_test(() => getEmptyHealthThermometerDevice()
+ .then(({device}) => {
+ promise = assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService('health_thermometer'),
+ expected);
+ // Disconnect called to clear attributeInstanceMap and allow the
+ // object to get garbage collected.
+ device.gatt.disconnect();
+ return garbageCollect();
+ })
+ .then(() => promise),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.https.window.js
new file mode 100644
index 0000000000..8e278af224
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.https.window.js
@@ -0,0 +1,28 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Garbage Collection ran during a getPrimaryService call that ' +
+ 'succeeds. Should not crash.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. ' +
+ '(Re)connect first with `device.gatt.connect`.',
+ 'NetworkError');
+let promise;
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}]
+ })
+ .then(({device}) => {
+ promise = assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService('health_thermometer'),
+ expected);
+ device.gatt.disconnect();
+ return garbageCollect();
+ })
+ .then(() => promise),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.https.window.js
new file mode 100644
index 0000000000..d4557f6753
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.https.window.js
@@ -0,0 +1,39 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Calls to getPrimaryService after a disconnection should return ' +
+ 'a different object.';
+let device, services_first_connection, services_second_connection;
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ })
+ .then(_ => ({device} = _))
+ .then(() => device.gatt.getPrimaryService('health_thermometer'))
+ .then(services => services_first_connection = services)
+ .then(() => device.gatt.disconnect())
+ .then(() => device.gatt.connect())
+ .then(() => device.gatt.getPrimaryService('health_thermometer'))
+ .then(services => services_second_connection = services)
+ .then(() => {
+ // Convert to arrays if necessary.
+ services_first_connection = [].concat(services_first_connection);
+ services_second_connection = [].concat(services_second_connection);
+
+ assert_equals(services_first_connection.length,
+ services_second_connection.length);
+
+ let first_connection_set = new Set(services_first_connection);
+ let second_connection_set = new Set(services_second_connection);
+
+ // The two sets should be disjoint.
+ let common_services = services_first_connection.filter(
+ val => second_connection_set.has(val));
+ assert_equals(common_services.length, 0);
+ }), test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-get-same-object.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-get-same-object.https.window.js
new file mode 100644
index 0000000000..b43cefb567
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-get-same-object.https.window.js
@@ -0,0 +1,37 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Calls to getPrimaryService should return the same object.';
+let device;
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']})
+ .then(({device}) => Promise.all([
+ device.gatt.getPrimaryService('health_thermometer'),
+ device.gatt.getPrimaryService('health_thermometer')]))
+ .then(([services_first_call, services_second_call]) => {
+ // Convert to arrays if necessary.
+ services_first_call = [].concat(services_first_call);
+ services_second_call = [].concat(services_second_call);
+
+ assert_equals(services_first_call.length, services_second_call.length);
+
+ let first_call_set = new Set(services_first_call);
+ assert_equals(services_first_call.length, first_call_set.size);
+ let second_call_set = new Set(services_second_call);
+ assert_equals(services_second_call.length, second_call_set.size);
+
+ services_first_call.forEach(service => {
+ assert_true(second_call_set.has(service))
+ });
+
+ services_second_call.forEach(service => {
+ assert_true(first_call_set.has(service));
+ });
+ }), test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-invalid-service-name.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-invalid-service-name.https.window.js
new file mode 100644
index 0000000000..cf4ab6c665
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-invalid-service-name.https.window.js
@@ -0,0 +1,26 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Wrong Service name. Reject with TypeError.';
+const expected = new DOMException(
+ "Failed to execute 'getPrimaryService' on " +
+ "'BluetoothRemoteGATTServer': Invalid Service name: " +
+ "'wrong_name'. It must be a valid UUID alias (e.g. 0x1234), " +
+ "UUID (lowercase hex characters e.g. " +
+ "'00001234-0000-1000-8000-00805f9b34fb'), " +
+ "or recognized standard name from " +
+ "https://www.bluetooth.com/specifications/gatt/services" +
+ " e.g. 'alert_notification'.",
+ 'TypeError');
+
+bluetooth_test(() => getConnectedHealthThermometerDevice()
+ .then(({device}) => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService('wrong_name'),
+ expected,
+ 'Wrong Service name passed.')),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-no-permission-absent-service.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-no-permission-absent-service.https.window.js
new file mode 100644
index 0000000000..3466ded4f9
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-no-permission-absent-service.https.window.js
@@ -0,0 +1,27 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Request for absent service without permission. ' +
+ 'Reject with SecurityError.';
+const expected = new DOMException(
+ 'Origin is not allowed to access the service. Tip: Add the service UUID ' +
+ 'to \'optionalServices\' in requestDevice() options. ' +
+ 'https://goo.gl/HxfxSQ',
+ 'SecurityError');
+
+bluetooth_test(() => getConnectedHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}]
+ })
+ .then(({device}) => Promise.all([
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService(glucose.alias), expected),
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService(glucose.name), expected),
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService(glucose.uuid), expected)])),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.https.window.js
new file mode 100644
index 0000000000..6576ef20a3
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.https.window.js
@@ -0,0 +1,21 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Request for present service without permission to access ' +
+ 'any service. Reject with SecurityError.';
+const expected = new DOMException(
+ 'Origin is not allowed to access any service. Tip: Add the service ' +
+ 'UUID to \'optionalServices\' in requestDevice() options. ' +
+ 'https://goo.gl/HxfxSQ',
+ 'SecurityError');
+
+bluetooth_test(() => getConnectedHealthThermometerDevice({acceptAllDevices: true})
+ .then(({device}) => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService('heart_rate'),
+ expected)),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-no-permission-present-service.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-no-permission-present-service.https.window.js
new file mode 100644
index 0000000000..3d0b460bc3
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-no-permission-present-service.https.window.js
@@ -0,0 +1,26 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Request for present service without permission. ' +
+ 'Reject with SecurityError.';
+const expected = new DOMException(
+ 'Origin is not allowed to access the service. Tip: Add the service UUID ' +
+ 'to \'optionalServices\' in requestDevice() options. https://goo.gl/HxfxSQ',
+ 'SecurityError');
+
+bluetooth_test(() => getConnectedHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}]
+ })
+ .then(({device}) => Promise.all([
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService(generic_access.alias), expected),
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService(generic_access.name), expected),
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService(generic_access.uuid), expected)])),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-service-not-found.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-service-not-found.https.window.js
new file mode 100644
index 0000000000..6e0d2c446b
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/gen-service-not-found.https.window.js
@@ -0,0 +1,20 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Request for absent service. Reject with NotFoundError.';
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['glucose']
+ })
+ .then(({device}) => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService('glucose'),
+ new DOMException(
+ `No Services matching UUID ${glucose.uuid} found in Device.`,
+ 'NotFoundError'))),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/service-found.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/service-found.https.window.js
new file mode 100644
index 0000000000..b8a930d10c
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/service-found.https.window.js
@@ -0,0 +1,28 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Request for service. Should return right service';
+
+bluetooth_test(async () => {
+ let {device} = await getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ });
+ let services = await Promise.all([
+ device.gatt.getPrimaryService(generic_access.alias),
+ device.gatt.getPrimaryService(generic_access.name),
+ device.gatt.getPrimaryService(generic_access.uuid)
+ ]);
+ services.forEach(service => {
+ assert_equals(
+ service.uuid, generic_access.uuid,
+ 'Service UUID should be the same as requested UUID.');
+ assert_true(
+ service.isPrimary,
+ 'getPrimaryService should return a primary service.');
+ assert_equals(
+ service.device, device, 'Service device should be the same as device.');
+ })
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.window.js
new file mode 100644
index 0000000000..b7f23a1491
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.window.js
@@ -0,0 +1,88 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Two iframes in the same origin should be able to access ' +
+ 'each other\'s services';
+
+const iframe1 = document.createElement('iframe');
+const iframe2 = document.createElement('iframe');
+
+function add_iframe(iframe) {
+ let promise =
+ new Promise(resolve => iframe.addEventListener('load', resolve));
+ iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
+ document.body.appendChild(iframe);
+ return promise;
+}
+
+function send_message(iframe, command, arg, assert_func) {
+ let promise = new Promise((resolve, reject) => {
+ window.addEventListener('message', (messageEvent) => {
+ try {
+ assert_func(messageEvent.data);
+ } catch (e) {
+ reject(e);
+ }
+ resolve();
+ }, {once: true});
+ });
+ if (command === 'RequestAndConnect') {
+ arg = {filters: [{services: [arg]}]};
+ }
+ callWithTrustedClick(
+ () => iframe.contentWindow.postMessage(
+ {
+ type: command,
+ options: arg,
+ },
+ '*'));
+ return promise;
+}
+
+bluetooth_test(async () => {
+ await getHealthThermometerDevice();
+ // 1. Add the first iframe.
+ await add_iframe(iframe1);
+ // 2. Connect with the first iframe, requesting the health
+ // thermometer service.
+ await send_message(
+ iframe1, 'RequestAndConnect', 'health_thermometer',
+ msg => assert_equals(msg, 'Connected'));
+ // 3. Access the health thermometer service with the first iframe
+ // (successfully).
+ await send_message(
+ iframe1, 'GetService', 'health_thermometer',
+ msg => assert_equals(msg, 'ServiceReceived'));
+ // 4. Access the generic access service with the first iframe
+ // (unsuccessfully).
+ await send_message(iframe1, 'GetService', 'generic_access', msg => {
+ let split_msg = msg.split(': ');
+ assert_equals(split_msg[0], 'FAIL');
+ assert_equals(split_msg[1], 'SecurityError');
+ });
+ // 5. Add the second iframe.
+ await add_iframe(iframe2);
+ // 6. Connect with the second iframe, requesting the generic
+ // access service.
+ await send_message(
+ iframe2, 'RequestAndConnect', 'generic_access',
+ msg => assert_equals(msg, 'Connected'));
+ // 7. Access the health thermometer service with the second iframe
+ // (successfully). Both iframes should have access to both
+ // services at this point since they have the same origin.
+ await send_message(
+ iframe2, 'GetService', 'health_thermometer',
+ msg => assert_equals(msg, 'ServiceReceived'));
+ // 8. Access the generic access service with the second iframe
+ // (unsuccessfully).
+ await send_message(
+ iframe2, 'GetService', 'generic_access',
+ msg => assert_equals(msg, 'ServiceReceived'));
+ // 9. Access the generic access service with the first iframe
+ // (successfully).
+ await send_message(
+ iframe1, 'GetService', 'generic_access',
+ msg => assert_equals(msg, 'ServiceReceived'));
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.window.js
new file mode 100644
index 0000000000..ccc913e5bf
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.window.js
@@ -0,0 +1,20 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Request for services. Does not return blocklisted service.';
+const expected = new DOMException(
+ 'Origin is not allowed to access the service. Tip: Add the service ' +
+ 'UUID to \'optionalServices\' in requestDevice() options. ' +
+ 'https://goo.gl/HxfxSQ',
+ 'SecurityError');
+
+bluetooth_test(async () => {
+ let {device} = await getConnectedHIDDevice({
+ filters: [{services: ['device_information']}],
+ optionalServices: ['human_interface_device']
+ });
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices('human_interface_device'), expected)
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/blocklisted-services.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/blocklisted-services.https.window.js
new file mode 100644
index 0000000000..ae6be90994
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/blocklisted-services.https.window.js
@@ -0,0 +1,22 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Request for services. Does not return blocklisted service.';
+
+bluetooth_test(async () => {
+ let {device} = await getHIDDevice({
+ filters: [{services: ['device_information']}],
+ optionalServices: ['generic_access', 'human_interface_device']
+ })
+ let services = await device.gatt.getPrimaryServices();
+ assert_equals(services.length, 2);
+ let uuid_set = new Set(services.map(s => s.uuid));
+
+ assert_equals(uuid_set.size, 2);
+ assert_true(uuid_set.has(BluetoothUUID.getService('generic_access')));
+ assert_true(uuid_set.has(BluetoothUUID.getService('device_information')));
+ assert_false(
+ uuid_set.has(BluetoothUUID.getService('human_interface_device')));
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/correct-services.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/correct-services.https.window.js
new file mode 100644
index 0000000000..f3d883dd2e
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/correct-services.https.window.js
@@ -0,0 +1,30 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Find correct services with UUID.';
+let device, fake_peripheral;
+
+bluetooth_test(async () => {
+ let {device, fake_peripheral} = await getConnectedHealthThermometerDevice(
+ {filters: [{services: ['health_thermometer']}]});
+ let fake_service =
+ await fake_peripheral.addFakeService({uuid: 'health_thermometer'});
+ await Promise.all([
+ fake_service.addFakeCharacteristic(
+ {uuid: 'temperature_measurement', properties: ['indicate']}),
+ fake_service.addFakeCharacteristic(
+ {uuid: 'temperature_measurement', properties: ['indicate']})
+ ]);
+ await fake_peripheral.setNextGATTDiscoveryResponse({code: HCI_SUCCESS});
+ let services = await device.gatt.getPrimaryServices('health_thermometer');
+ let [characteristics1, characteristics2] = await Promise.all(
+ [services[0].getCharacteristics(), services[1].getCharacteristics()]);
+ if (characteristics1.length === 2)
+ assert_equals(characteristics2.length, 3);
+ else if (characteristics2.length === 2)
+ assert_equals(characteristics1.length, 3);
+ else
+ assert_unreached('Invalid lengths.');
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.https.window.js
new file mode 100644
index 0000000000..21b561375d
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.https.window.js
@@ -0,0 +1,26 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'disconnect() called before getPrimaryServices. ' +
+ 'Reject with NetworkError.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect ' +
+ 'first with `device.gatt.connect`.',
+ 'NetworkError');
+let device;
+
+bluetooth_test(() => getConnectedHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ })
+ .then(_ => ({device} = _))
+ .then(() => device.gatt.disconnect())
+ .then(() => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices('health_thermometer'),
+ expected)),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before.https.window.js
new file mode 100644
index 0000000000..8e5fea83ab
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before.https.window.js
@@ -0,0 +1,26 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'disconnect() called before getPrimaryServices. ' +
+ 'Reject with NetworkError.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect ' +
+ 'first with `device.gatt.connect`.',
+ 'NetworkError');
+let device;
+
+bluetooth_test(() => getConnectedHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ })
+ .then(_ => ({device} = _))
+ .then(() => device.gatt.disconnect())
+ .then(() => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(),
+ expected)),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.https.window.js
new file mode 100644
index 0000000000..5c28716b90
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.https.window.js
@@ -0,0 +1,25 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'disconnect() called during a getPrimaryServices ' +
+ 'call that fails. Reject with NetworkError.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect ' +
+ 'first with `device.gatt.connect`.', 'NetworkError');
+let device;
+
+bluetooth_test(() => getEmptyHealthThermometerDevice()
+ .then(_ => ({device} = _))
+ .then(() => {
+ let promise = assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices('health_thermometer'),
+ expected)
+ device.gatt.disconnect();
+ return promise;
+ }),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error.https.window.js
new file mode 100644
index 0000000000..ddc3124791
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error.https.window.js
@@ -0,0 +1,25 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'disconnect() called during a getPrimaryServices ' +
+ 'call that fails. Reject with NetworkError.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect ' +
+ 'first with `device.gatt.connect`.', 'NetworkError');
+let device;
+
+bluetooth_test(() => getEmptyHealthThermometerDevice()
+ .then(_ => ({device} = _))
+ .then(() => {
+ let promise = assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(),
+ expected)
+ device.gatt.disconnect();
+ return promise;
+ }),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success-with-uuid.https.window.js
new file mode 100644
index 0000000000..13e3806d31
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success-with-uuid.https.window.js
@@ -0,0 +1,26 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'disconnect() called during a getPrimaryServices call that ' +
+ 'succeeds. Reject with NetworkError.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect ' +
+ 'first with `device.gatt.connect`.',
+ 'NetworkError');
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ })
+ .then(({device}) => {
+ let promise = assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices('health_thermometer'),
+ expected);
+ device.gatt.disconnect();
+ return promise;
+ }), test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success.https.window.js
new file mode 100644
index 0000000000..d6b31936c6
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success.https.window.js
@@ -0,0 +1,26 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'disconnect() called during a getPrimaryServices call that ' +
+ 'succeeds. Reject with NetworkError.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect ' +
+ 'first with `device.gatt.connect`.',
+ 'NetworkError');
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ })
+ .then(({device}) => {
+ let promise = assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(),
+ expected);
+ device.gatt.disconnect();
+ return promise;
+ }), test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-discovery-timeout-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-discovery-timeout-with-uuid.https.window.js
new file mode 100644
index 0000000000..77f7bc81d9
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-discovery-timeout-with-uuid.https.window.js
@@ -0,0 +1,46 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc =
+ 'Calls to getPrimaryServices when device disconnects and discovery' +
+ ' times out should reject promise rather than get stuck.';
+let device;
+
+bluetooth_test(
+ async (t) => {
+ let {device, fake_peripheral} =
+ await getConnectedHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ });
+
+ await fake_peripheral.setNextGATTDiscoveryResponse({
+ code: HCI_CONNECTION_TIMEOUT,
+ });
+ await Promise.all([
+ fake_peripheral.simulateGATTDisconnection({
+ code: HCI_SUCCESS,
+ }),
+ // Using promise_rejects_dom here rather than
+ // assert_promise_rejects_with_message as the race between
+ // simulateGATTDisconnection and getPrimaryServices might end up giving
+ // slightly different exception message (i.e has "Failed to execute ...
+ // on
+ // ... " prefix when disconnected state is reflected on the renderer
+ // side). The point of the test is no matter how race between them, the
+ // promise will be rejected as opposed to get stuck.
+ promise_rejects_dom(t, 'NetworkError', device.gatt.getPrimaryServices('health_thermometer')),
+ ]);
+ },
+ test_desc, '',
+ // As specified above there is a race condition between
+ // simulateGATTDisconnection and getPrimaryServices, the artificial
+ // GATTDiscoveryResponse might not be consumed in case
+ // simulateGATTDisconnection happens first. As a result explicitly skip
+ // all response consumed validation at the end of the test.
+ /*validate_response_consumed=*/ false);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-discovery-timeout.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-discovery-timeout.https.window.js
new file mode 100644
index 0000000000..ea55b7b495
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-discovery-timeout.https.window.js
@@ -0,0 +1,46 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc =
+ 'Calls to getPrimaryServices when device disconnects and discovery' +
+ ' times out should reject promise rather than get stuck.';
+let device;
+
+bluetooth_test(
+ async (t) => {
+ let {device, fake_peripheral} =
+ await getConnectedHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ });
+
+ await fake_peripheral.setNextGATTDiscoveryResponse({
+ code: HCI_CONNECTION_TIMEOUT,
+ });
+ await Promise.all([
+ fake_peripheral.simulateGATTDisconnection({
+ code: HCI_SUCCESS,
+ }),
+ // Using promise_rejects_dom here rather than
+ // assert_promise_rejects_with_message as the race between
+ // simulateGATTDisconnection and getPrimaryServices might end up giving
+ // slightly different exception message (i.e has "Failed to execute ...
+ // on
+ // ... " prefix when disconnected state is reflected on the renderer
+ // side). The point of the test is no matter how race between them, the
+ // promise will be rejected as opposed to get stuck.
+ promise_rejects_dom(t, 'NetworkError', device.gatt.getPrimaryServices()),
+ ]);
+ },
+ test_desc, '',
+ // As specified above there is a race condition between
+ // simulateGATTDisconnection and getPrimaryServices, the artificial
+ // GATTDiscoveryResponse might not be consumed in case
+ // simulateGATTDisconnection happens first. As a result explicitly skip
+ // all response consumed validation at the end of the test.
+ /*validate_response_consumed=*/ false);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.https.window.js
new file mode 100644
index 0000000000..8cdb83e3ad
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.https.window.js
@@ -0,0 +1,43 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Calls on services after we disconnect and connect again. '+
+ 'Should reject with InvalidStateError.';
+let device, services;
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}]
+ })
+ .then(_ => ({device} = _))
+ .then(() => device.gatt.getPrimaryServices('health_thermometer'))
+ // Convert to array if necessary.
+ .then(s => services = [].concat(s))
+ .then(() => device.gatt.disconnect())
+ .then(() => device.gatt.connect())
+ .then(() => {
+ let promises = Promise.resolve();
+ for (let service of services) {
+ let error = new DOMException(
+ `Service with UUID ${service.uuid} is no longer valid. Remember ` +
+ `to retrieve the service again after reconnecting.`,
+ 'InvalidStateError');
+ promises = promises.then(() =>
+ assert_promise_rejects_with_message(
+ service.getCharacteristic('measurement_interval'),
+ error));
+ promises = promises.then(() =>
+ assert_promise_rejects_with_message(
+ service.getCharacteristics(),
+ error));
+ promises = promises.then(() =>
+ assert_promise_rejects_with_message(
+ service.getCharacteristics('measurement_interval'),
+ error));
+ }
+ return promises;
+ }), test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.https.window.js
new file mode 100644
index 0000000000..9fd536f051
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.https.window.js
@@ -0,0 +1,43 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Calls on services after we disconnect and connect again. '+
+ 'Should reject with InvalidStateError.';
+let device, services;
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}]
+ })
+ .then(_ => ({device} = _))
+ .then(() => device.gatt.getPrimaryServices())
+ // Convert to array if necessary.
+ .then(s => services = [].concat(s))
+ .then(() => device.gatt.disconnect())
+ .then(() => device.gatt.connect())
+ .then(() => {
+ let promises = Promise.resolve();
+ for (let service of services) {
+ let error = new DOMException(
+ `Service with UUID ${service.uuid} is no longer valid. Remember ` +
+ `to retrieve the service again after reconnecting.`,
+ 'InvalidStateError');
+ promises = promises.then(() =>
+ assert_promise_rejects_with_message(
+ service.getCharacteristic('measurement_interval'),
+ error));
+ promises = promises.then(() =>
+ assert_promise_rejects_with_message(
+ service.getCharacteristics(),
+ error));
+ promises = promises.then(() =>
+ assert_promise_rejects_with_message(
+ service.getCharacteristics('measurement_interval'),
+ error));
+ }
+ return promises;
+ }), test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnected-device-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnected-device-with-uuid.https.window.js
new file mode 100644
index 0000000000..e0393d5e69
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnected-device-with-uuid.https.window.js
@@ -0,0 +1,23 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'getPrimaryServices called before connecting. Reject with ' +
+ 'NetworkError.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect ' +
+ 'first with `device.gatt.connect`.',
+ 'NetworkError');
+
+bluetooth_test(() => getDiscoveredHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ })
+ .then(({device}) => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices('health_thermometer'),
+ expected)),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnected-device.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnected-device.https.window.js
new file mode 100644
index 0000000000..87d74c6ab1
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-disconnected-device.https.window.js
@@ -0,0 +1,23 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'getPrimaryServices called before connecting. Reject with ' +
+ 'NetworkError.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect ' +
+ 'first with `device.gatt.connect`.',
+ 'NetworkError');
+
+bluetooth_test(() => getDiscoveredHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ })
+ .then(({device}) => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(),
+ expected)),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-discovery-complete-no-permission-absent-service-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-discovery-complete-no-permission-absent-service-with-uuid.https.window.js
new file mode 100644
index 0000000000..6e179dc5d9
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-discovery-complete-no-permission-absent-service-with-uuid.https.window.js
@@ -0,0 +1,29 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Request for absent service without permission. Should ' +
+ 'Reject with SecurityError even if services have been discovered already.';
+const expected = new DOMException(
+ 'Origin is not allowed to access the service. Tip: Add the service ' +
+ 'UUID to \'optionalServices\' in requestDevice() options. ' +
+ 'https://goo.gl/HxfxSQ',
+ 'SecurityError');
+let device;
+
+bluetooth_test(() => getHealthThermometerDeviceWithServicesDiscovered({
+ filters: [{services: ['health_thermometer']}]
+ })
+ .then(_ => ({device} = _))
+ .then(() => Promise.all([
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(glucose.alias), expected),
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(glucose.name), expected),
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(glucose.uuid), expected)])),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-discovery-complete-service-not-found-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-discovery-complete-service-not-found-with-uuid.https.window.js
new file mode 100644
index 0000000000..66cfb491c0
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-discovery-complete-service-not-found-with-uuid.https.window.js
@@ -0,0 +1,20 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Request for absent service. Must reject with ' +
+ 'NotFoundError even when the services have previously been discovered.';
+
+bluetooth_test(() => getHealthThermometerDeviceWithServicesDiscovered({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['glucose']})
+ .then(({device}) => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices('glucose'),
+ new DOMException(
+ `No Services matching UUID ${glucose.uuid} found in Device.`,
+ 'NotFoundError'))),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.https.window.js
new file mode 100644
index 0000000000..a235cf5d18
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.https.window.js
@@ -0,0 +1,28 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Garbage Collection ran during a getPrimaryServices ' +
+ 'call that failed. Should not crash.'
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect first ' +
+ 'with `device.gatt.connect`.',
+ 'NetworkError');
+let promise;
+
+bluetooth_test(() => getEmptyHealthThermometerDevice()
+ .then(({device}) => {
+ promise = assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices('health_thermometer'),
+ expected);
+ // Disconnect called to clear attributeInstanceMap and allow the
+ // object to get garbage collected.
+ device.gatt.disconnect();
+ return garbageCollect();
+ })
+ .then(() => promise),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error.https.window.js
new file mode 100644
index 0000000000..f174d4aef9
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error.https.window.js
@@ -0,0 +1,28 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Garbage Collection ran during a getPrimaryServices ' +
+ 'call that failed. Should not crash.'
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. (Re)connect first ' +
+ 'with `device.gatt.connect`.',
+ 'NetworkError');
+let promise;
+
+bluetooth_test(() => getEmptyHealthThermometerDevice()
+ .then(({device}) => {
+ promise = assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(),
+ expected);
+ // Disconnect called to clear attributeInstanceMap and allow the
+ // object to get garbage collected.
+ device.gatt.disconnect();
+ return garbageCollect();
+ })
+ .then(() => promise),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.https.window.js
new file mode 100644
index 0000000000..cf5dfb246f
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.https.window.js
@@ -0,0 +1,28 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Garbage Collection ran during a getPrimaryServices call that ' +
+ 'succeeds. Should not crash.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. ' +
+ '(Re)connect first with `device.gatt.connect`.',
+ 'NetworkError');
+let promise;
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}]
+ })
+ .then(({device}) => {
+ promise = assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices('health_thermometer'),
+ expected);
+ device.gatt.disconnect();
+ return garbageCollect();
+ })
+ .then(() => promise),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.https.window.js
new file mode 100644
index 0000000000..f1c080a946
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.https.window.js
@@ -0,0 +1,28 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Garbage Collection ran during a getPrimaryServices call that ' +
+ 'succeeds. Should not crash.';
+const expected = new DOMException(
+ 'GATT Server is disconnected. Cannot retrieve services. ' +
+ '(Re)connect first with `device.gatt.connect`.',
+ 'NetworkError');
+let promise;
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}]
+ })
+ .then(({device}) => {
+ promise = assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(),
+ expected);
+ device.gatt.disconnect();
+ return garbageCollect();
+ })
+ .then(() => promise),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.https.window.js
new file mode 100644
index 0000000000..2e40d580f3
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.https.window.js
@@ -0,0 +1,39 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Calls to getPrimaryServices after a disconnection should return ' +
+ 'a different object.';
+let device, services_first_connection, services_second_connection;
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ })
+ .then(_ => ({device} = _))
+ .then(() => device.gatt.getPrimaryServices('health_thermometer'))
+ .then(services => services_first_connection = services)
+ .then(() => device.gatt.disconnect())
+ .then(() => device.gatt.connect())
+ .then(() => device.gatt.getPrimaryServices('health_thermometer'))
+ .then(services => services_second_connection = services)
+ .then(() => {
+ // Convert to arrays if necessary.
+ services_first_connection = [].concat(services_first_connection);
+ services_second_connection = [].concat(services_second_connection);
+
+ assert_equals(services_first_connection.length,
+ services_second_connection.length);
+
+ let first_connection_set = new Set(services_first_connection);
+ let second_connection_set = new Set(services_second_connection);
+
+ // The two sets should be disjoint.
+ let common_services = services_first_connection.filter(
+ val => second_connection_set.has(val));
+ assert_equals(common_services.length, 0);
+ }), test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.https.window.js
new file mode 100644
index 0000000000..ee1fc971bf
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.https.window.js
@@ -0,0 +1,39 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Calls to getPrimaryServices after a disconnection should return ' +
+ 'a different object.';
+let device, services_first_connection, services_second_connection;
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ })
+ .then(_ => ({device} = _))
+ .then(() => device.gatt.getPrimaryServices())
+ .then(services => services_first_connection = services)
+ .then(() => device.gatt.disconnect())
+ .then(() => device.gatt.connect())
+ .then(() => device.gatt.getPrimaryServices())
+ .then(services => services_second_connection = services)
+ .then(() => {
+ // Convert to arrays if necessary.
+ services_first_connection = [].concat(services_first_connection);
+ services_second_connection = [].concat(services_second_connection);
+
+ assert_equals(services_first_connection.length,
+ services_second_connection.length);
+
+ let first_connection_set = new Set(services_first_connection);
+ let second_connection_set = new Set(services_second_connection);
+
+ // The two sets should be disjoint.
+ let common_services = services_first_connection.filter(
+ val => second_connection_set.has(val));
+ assert_equals(common_services.length, 0);
+ }), test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.https.window.js
new file mode 100644
index 0000000000..b589056a23
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.https.window.js
@@ -0,0 +1,37 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Calls to getPrimaryServices should return the same object.';
+let device;
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']})
+ .then(({device}) => Promise.all([
+ device.gatt.getPrimaryServices('health_thermometer'),
+ device.gatt.getPrimaryServices('health_thermometer')]))
+ .then(([services_first_call, services_second_call]) => {
+ // Convert to arrays if necessary.
+ services_first_call = [].concat(services_first_call);
+ services_second_call = [].concat(services_second_call);
+
+ assert_equals(services_first_call.length, services_second_call.length);
+
+ let first_call_set = new Set(services_first_call);
+ assert_equals(services_first_call.length, first_call_set.size);
+ let second_call_set = new Set(services_second_call);
+ assert_equals(services_second_call.length, second_call_set.size);
+
+ services_first_call.forEach(service => {
+ assert_true(second_call_set.has(service))
+ });
+
+ services_second_call.forEach(service => {
+ assert_true(first_call_set.has(service));
+ });
+ }), test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-same-object.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-same-object.https.window.js
new file mode 100644
index 0000000000..63739add91
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-get-same-object.https.window.js
@@ -0,0 +1,37 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Calls to getPrimaryServices should return the same object.';
+let device;
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']})
+ .then(({device}) => Promise.all([
+ device.gatt.getPrimaryServices(),
+ device.gatt.getPrimaryServices()]))
+ .then(([services_first_call, services_second_call]) => {
+ // Convert to arrays if necessary.
+ services_first_call = [].concat(services_first_call);
+ services_second_call = [].concat(services_second_call);
+
+ assert_equals(services_first_call.length, services_second_call.length);
+
+ let first_call_set = new Set(services_first_call);
+ assert_equals(services_first_call.length, first_call_set.size);
+ let second_call_set = new Set(services_second_call);
+ assert_equals(services_second_call.length, second_call_set.size);
+
+ services_first_call.forEach(service => {
+ assert_true(second_call_set.has(service))
+ });
+
+ services_second_call.forEach(service => {
+ assert_true(first_call_set.has(service));
+ });
+ }), test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.https.window.js
new file mode 100644
index 0000000000..a9b1262e6a
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.https.window.js
@@ -0,0 +1,26 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Wrong Service name. Reject with TypeError.';
+const expected = new DOMException(
+ "Failed to execute 'getPrimaryServices' on " +
+ "'BluetoothRemoteGATTServer': Invalid Service name: " +
+ "'wrong_name'. It must be a valid UUID alias (e.g. 0x1234), " +
+ "UUID (lowercase hex characters e.g. " +
+ "'00001234-0000-1000-8000-00805f9b34fb'), " +
+ "or recognized standard name from " +
+ "https://www.bluetooth.com/specifications/gatt/services" +
+ " e.g. 'alert_notification'.",
+ 'TypeError');
+
+bluetooth_test(() => getConnectedHealthThermometerDevice()
+ .then(({device}) => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices('wrong_name'),
+ expected,
+ 'Wrong Service name passed.')),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-absent-service-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-absent-service-with-uuid.https.window.js
new file mode 100644
index 0000000000..27ad9f008e
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-absent-service-with-uuid.https.window.js
@@ -0,0 +1,27 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Request for absent service without permission. ' +
+ 'Reject with SecurityError.';
+const expected = new DOMException(
+ 'Origin is not allowed to access the service. Tip: Add the service UUID ' +
+ 'to \'optionalServices\' in requestDevice() options. ' +
+ 'https://goo.gl/HxfxSQ',
+ 'SecurityError');
+
+bluetooth_test(() => getConnectedHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}]
+ })
+ .then(({device}) => Promise.all([
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(glucose.alias), expected),
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(glucose.name), expected),
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(glucose.uuid), expected)])),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.https.window.js
new file mode 100644
index 0000000000..d5f06c23da
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.https.window.js
@@ -0,0 +1,21 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Request for present service without permission to access ' +
+ 'any service. Reject with SecurityError.';
+const expected = new DOMException(
+ 'Origin is not allowed to access any service. Tip: Add the service ' +
+ 'UUID to \'optionalServices\' in requestDevice() options. ' +
+ 'https://goo.gl/HxfxSQ',
+ 'SecurityError');
+
+bluetooth_test(() => getConnectedHealthThermometerDevice({acceptAllDevices: true})
+ .then(({device}) => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices('heart_rate'),
+ expected)),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.https.window.js
new file mode 100644
index 0000000000..8aa730d2ed
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.https.window.js
@@ -0,0 +1,21 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Request for present service without permission to access ' +
+ 'any service. Reject with SecurityError.';
+const expected = new DOMException(
+ 'Origin is not allowed to access any service. Tip: Add the service ' +
+ 'UUID to \'optionalServices\' in requestDevice() options. ' +
+ 'https://goo.gl/HxfxSQ',
+ 'SecurityError');
+
+bluetooth_test(() => getConnectedHealthThermometerDevice({acceptAllDevices: true})
+ .then(({device}) => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(),
+ expected)),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.https.window.js
new file mode 100644
index 0000000000..a2047a0e8f
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.https.window.js
@@ -0,0 +1,26 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Request for present service without permission. ' +
+ 'Reject with SecurityError.';
+const expected = new DOMException(
+ 'Origin is not allowed to access the service. Tip: Add the service UUID ' +
+ 'to \'optionalServices\' in requestDevice() options. https://goo.gl/HxfxSQ',
+ 'SecurityError');
+
+bluetooth_test(() => getConnectedHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}]
+ })
+ .then(({device}) => Promise.all([
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(generic_access.alias), expected),
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(generic_access.name), expected),
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(generic_access.uuid), expected)])),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.https.window.js
new file mode 100644
index 0000000000..a2db1edc4b
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.https.window.js
@@ -0,0 +1,20 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/gc.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+// Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py
+'use strict';
+const test_desc = 'Request for absent service. Reject with NotFoundError.';
+
+bluetooth_test(() => getHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['glucose']
+ })
+ .then(({device}) => assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices('glucose'),
+ new DOMException(
+ `No Services matching UUID ${glucose.uuid} found in Device.`,
+ 'NotFoundError'))),
+ test_desc);
+
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.window.js
new file mode 100644
index 0000000000..972e6a75ca
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/services-found-with-uuid.https.window.js
@@ -0,0 +1,25 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Request for services. Should return right number of ' +
+ 'services.';
+
+bluetooth_test(async () => {
+ let {device} = await getTwoHealthThermometerServicesDevice(
+ {filters: [{services: ['health_thermometer']}]});
+ let services_arrays = await Promise.all([
+ device.gatt.getPrimaryServices(health_thermometer.alias),
+ device.gatt.getPrimaryServices(health_thermometer.name),
+ device.gatt.getPrimaryServices(health_thermometer.uuid)
+ ]);
+ services_arrays.forEach(services => {
+ assert_equals(services.length, 2);
+ services.forEach(service => {
+ assert_equals(
+ service.uuid, BluetoothUUID.getService('health_thermometer'));
+ assert_true(service.isPrimary);
+ });
+ })
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/services-found.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/services-found.https.window.js
new file mode 100644
index 0000000000..46861175c6
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/services-found.https.window.js
@@ -0,0 +1,25 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Find all services in a device.';
+
+bluetooth_test(async () => {
+ let {device} = await getTwoHealthThermometerServicesDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['generic_access']
+ });
+ let services = await device.gatt.getPrimaryServices();
+ // Expect three service instances.
+ assert_equals(services.length, 3);
+ services.forEach(s => assert_true(s.isPrimary));
+
+ let uuid_set = new Set(services.map(s => s.uuid));
+ // Two of the expected services are 'health_thermometer', so
+ // only 2 unique UUIDs.
+ assert_equals(uuid_set.size, 2);
+
+ assert_true(uuid_set.has(BluetoothUUID.getService('generic_access')));
+ assert_true(uuid_set.has(BluetoothUUID.getService('health_thermometer')));
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/server/getPrimaryServices/services-not-found.https.window.js b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/services-not-found.https.window.js
new file mode 100644
index 0000000000..6350328241
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/server/getPrimaryServices/services-not-found.https.window.js
@@ -0,0 +1,15 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Request for services in a device with no services. Reject ' +
+ 'with NotFoundError.';
+const expected =
+ new DOMException('No Services found in device.', 'NotFoundError');
+
+bluetooth_test(async () => {
+ let {device} = await getEmptyHealthThermometerDevice();
+ return assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(), expected);
+}, test_desc);