diff options
Diffstat (limited to '')
29 files changed, 688 insertions, 0 deletions
diff --git a/testing/web-platform/tests/bluetooth/script-tests/base_test_js.template b/testing/web-platform/tests/bluetooth/script-tests/base_test_js.template new file mode 100644 index 0000000000..04c7a70ba4 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/base_test_js.template @@ -0,0 +1,7 @@ +// 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 +TEST diff --git a/testing/web-platform/tests/bluetooth/script-tests/characteristic/characteristic-is-removed.js b/testing/web-platform/tests/bluetooth/script-tests/characteristic/characteristic-is-removed.js new file mode 100644 index 0000000000..48aaec3e93 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/characteristic/characteristic-is-removed.js @@ -0,0 +1,24 @@ +'use strict'; +const test_desc = 'Characteristic gets removed. Reject with InvalidStateError.'; +const expected = new DOMException('GATT Characteristic no longer exists.', + 'InvalidStateError'); +let fake_peripheral, characteristic, fake_characteristic; + +bluetooth_test(() => getMeasurementIntervalCharacteristic() + .then(_ => ({fake_peripheral, characteristic, fake_characteristic} = _)) + .then(() => characteristic.getDescriptor(user_description.name)) + .then(() => null, (e) => assert_unreached('Caught error unexpectedly.', e)) + .then(() => fake_characteristic.remove()) + .then(() => fake_peripheral.simulateGATTServicesChanged()) + .then(() => assert_promise_rejects_with_message( + characteristic.CALLS([ + getDescriptor(user_description.name)| + getDescriptors(user_description.name)[UUID]| + getDescriptors()| + readValue()| + writeValue(new Uint8Array(1))| + writeValueWithResponse(new Uint8Array(1))| + writeValueWithoutResponse(new Uint8Array(1))| + startNotifications() + ]), expected)), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/characteristic/descriptor-get-same-object.js b/testing/web-platform/tests/bluetooth/script-tests/characteristic/descriptor-get-same-object.js new file mode 100644 index 0000000000..4e6bc3519b --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/characteristic/descriptor-get-same-object.js @@ -0,0 +1,32 @@ +'use strict'; +const test_desc = 'Calls to FUNCTION_NAME should return the same object.'; +let characteristic; + +bluetooth_test(() => getMeasurementIntervalCharacteristic() + .then(_ => ({characteristic} = _)) + .then(() => Promise.all([ + characteristic.CALLS([ + getDescriptor(user_description.alias)| + getDescriptors(user_description.alias) + ]), + characteristic.FUNCTION_NAME(user_description.name), + characteristic.FUNCTION_NAME(user_description.uuid) + ])) + .then(descriptors_arrays => { + assert_true(descriptors_arrays.length > 0) + + // Convert to arrays if necessary. + for (let i = 0; i < descriptors_arrays.length; i++) { + descriptors_arrays[i] = [].concat(descriptors_arrays[i]); + } + + for (let i = 1; i < descriptors_arrays.length; i++) { + assert_equals(descriptors_arrays[0].length, + descriptors_arrays[i].length); + } + + let base_set = new Set(descriptors_arrays[0]); + for (let descriptors of descriptors_arrays) { + descriptors.forEach(descriptor => assert_true(base_set.has(descriptor))); + } + }), test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/characteristic/service-is-removed.js b/testing/web-platform/tests/bluetooth/script-tests/characteristic/service-is-removed.js new file mode 100644 index 0000000000..2f5824082b --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/characteristic/service-is-removed.js @@ -0,0 +1,20 @@ +// TODO(https://crbug.com/672127) Use this test case to test the rest of +// characteristic functions. +'use strict'; +const test_desc = 'Service is removed. Reject with InvalidStateError.'; +const expected = new DOMException('GATT Service no longer exists.', + 'InvalidStateError'); +let characteristic, fake_peripheral, fake_service; + +bluetooth_test(() => getMeasurementIntervalCharacteristic() + .then(_ => ({characteristic, fake_peripheral, fake_service} = _)) + .then(() => fake_service.remove()) + .then(() => fake_peripheral.simulateGATTServicesChanged()) + .then(() => assert_promise_rejects_with_message( + characteristic.CALLS([ + getDescriptor(user_description.name)| + getDescriptors(user_description.uuid)[UUID]| + getDescriptors(user_description.name)]), + expected, + 'Service got removed.')), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/descriptor/service-is-removed.js b/testing/web-platform/tests/bluetooth/script-tests/descriptor/service-is-removed.js new file mode 100644 index 0000000000..5373364399 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/descriptor/service-is-removed.js @@ -0,0 +1,18 @@ +'use strict'; +const test_desc = 'Service gets removed. Reject with InvalidStateError.'; +const expected = new DOMException('GATT Service no longer exists.', + 'InvalidStateError'); +let descriptor, fake_peripheral, fake_service; + +bluetooth_test(() => getUserDescriptionDescriptor() + .then(_ => ({descriptor, fake_peripheral, fake_service} = _)) + .then(() => fake_service.remove()) + .then(() => fake_peripheral.simulateGATTServicesChanged()) + .then(() => assert_promise_rejects_with_message( + descriptor.CALLS([ + readValue()| + writeValue(new ArrayBuffer(1 /* length */)) + ]), + expected, + 'Service got removed.')), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/server/disconnect-called-before.js b/testing/web-platform/tests/bluetooth/script-tests/server/disconnect-called-before.js new file mode 100644 index 0000000000..57704ee299 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/disconnect-called-before.js @@ -0,0 +1,22 @@ +'use strict'; +const test_desc = 'disconnect() called before FUNCTION_NAME. ' + + '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.CALLS([ + getPrimaryService('health_thermometer')| + getPrimaryServices()| + getPrimaryServices('health_thermometer')[UUID]]), + expected)), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/server/disconnect-called-during-error.js b/testing/web-platform/tests/bluetooth/script-tests/server/disconnect-called-during-error.js new file mode 100644 index 0000000000..edabb07bcc --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/disconnect-called-during-error.js @@ -0,0 +1,22 @@ +'use strict'; +const test_desc = 'disconnect() called during a FUNCTION_NAME ' + + '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.CALLS([ + getPrimaryService('health_thermometer')| + getPrimaryServices()| + getPrimaryServices('health_thermometer')[UUID] + ]), + expected) + device.gatt.disconnect(); + return promise; + }), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/server/disconnect-called-during-success.js b/testing/web-platform/tests/bluetooth/script-tests/server/disconnect-called-during-success.js new file mode 100644 index 0000000000..84157a0693 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/disconnect-called-during-success.js @@ -0,0 +1,23 @@ +'use strict'; +const test_desc = 'disconnect() called during a FUNCTION_NAME 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.CALLS([ + getPrimaryService('health_thermometer')| + getPrimaryServices()| + getPrimaryServices('health_thermometer')[UUID] + ]), + expected); + device.gatt.disconnect(); + return promise; + }), test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/server/disconnect-discovery-timeout.js b/testing/web-platform/tests/bluetooth/script-tests/server/disconnect-discovery-timeout.js new file mode 100644 index 0000000000..718e290950 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/disconnect-discovery-timeout.js @@ -0,0 +1,42 @@ +'use strict'; +const test_desc = + 'Calls to FUNCTION_NAME 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.CALLS([ + getPrimaryService('health_thermometer') | getPrimaryServices() | + getPrimaryServices('health_thermometer')[UUID] + ])), + ]); + }, + 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/script-tests/server/disconnect-invalidates-objects.js b/testing/web-platform/tests/bluetooth/script-tests/server/disconnect-invalidates-objects.js new file mode 100644 index 0000000000..995fda3441 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/disconnect-invalidates-objects.js @@ -0,0 +1,39 @@ +'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.CALLS([ + getPrimaryService('health_thermometer')| + getPrimaryServices()| + getPrimaryServices('health_thermometer')[UUID]])) + // 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/script-tests/server/disconnected-device.js b/testing/web-platform/tests/bluetooth/script-tests/server/disconnected-device.js new file mode 100644 index 0000000000..2b6011642b --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/disconnected-device.js @@ -0,0 +1,20 @@ +'use strict'; +const test_desc = 'FUNCTION_NAME 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.CALLS([ + getPrimaryService('health_thermometer')| + getPrimaryServices()| + getPrimaryServices('health_thermometer')[UUID] + ]), + expected)), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/server/discovery-complete-no-permission-absent-service.js b/testing/web-platform/tests/bluetooth/script-tests/server/discovery-complete-no-permission-absent-service.js new file mode 100644 index 0000000000..e9e972359a --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/discovery-complete-no-permission-absent-service.js @@ -0,0 +1,25 @@ +'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.CALLS([ + getPrimaryService(glucose.alias)| + getPrimaryServices(glucose.alias)[UUID] + ]), expected), + assert_promise_rejects_with_message( + device.gatt.FUNCTION_NAME(glucose.name), expected), + assert_promise_rejects_with_message( + device.gatt.FUNCTION_NAME(glucose.uuid), expected)])), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/server/discovery-complete-service-not-found.js b/testing/web-platform/tests/bluetooth/script-tests/server/discovery-complete-service-not-found.js new file mode 100644 index 0000000000..6b745d7e2a --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/discovery-complete-service-not-found.js @@ -0,0 +1,16 @@ +'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.CALLS([ + getPrimaryService('glucose')| + getPrimaryServices('glucose')[UUID] + ]), + new DOMException( + `No Services matching UUID ${glucose.uuid} found in Device.`, + 'NotFoundError'))), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/server/garbage-collection-ran-during-error.js b/testing/web-platform/tests/bluetooth/script-tests/server/garbage-collection-ran-during-error.js new file mode 100644 index 0000000000..cf508a928e --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/garbage-collection-ran-during-error.js @@ -0,0 +1,25 @@ +'use strict'; +const test_desc = 'Garbage Collection ran during a FUNCTION_NAME ' + + '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.CALLS([ + getPrimaryService('health_thermometer')| + getPrimaryServices()| + getPrimaryServices('health_thermometer')[UUID] + ]), + 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/script-tests/server/garbage-collection-ran-during-success.js b/testing/web-platform/tests/bluetooth/script-tests/server/garbage-collection-ran-during-success.js new file mode 100644 index 0000000000..bb472fcca4 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/garbage-collection-ran-during-success.js @@ -0,0 +1,24 @@ +'use strict'; +const test_desc = 'Garbage Collection ran during a FUNCTION_NAME 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.CALLS([ + getPrimaryService('health_thermometer') | + getPrimaryServices() | + getPrimaryServices('health_thermometer')[UUID]]), + expected); + device.gatt.disconnect(); + return garbageCollect(); + }) + .then(() => promise), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/server/get-different-service-after-reconnection.js b/testing/web-platform/tests/bluetooth/script-tests/server/get-different-service-after-reconnection.js new file mode 100644 index 0000000000..e72128a76f --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/get-different-service-after-reconnection.js @@ -0,0 +1,35 @@ +'use strict'; +const test_desc = 'Calls to FUNCTION_NAME 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.CALLS([ + getPrimaryService('health_thermometer')| + getPrimaryServices()| + getPrimaryServices('health_thermometer')[UUID]])) + .then(services => services_first_connection = services) + .then(() => device.gatt.disconnect()) + .then(() => device.gatt.connect()) + .then(() => device.gatt.PREVIOUS_CALL) + .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/script-tests/server/get-same-object.js b/testing/web-platform/tests/bluetooth/script-tests/server/get-same-object.js new file mode 100644 index 0000000000..3b3bdd19d2 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/get-same-object.js @@ -0,0 +1,33 @@ +'use strict'; +const test_desc = 'Calls to FUNCTION_NAME should return the same object.'; +let device; + +bluetooth_test(() => getHealthThermometerDevice({ + filters: [{services: ['health_thermometer']}], + optionalServices: ['generic_access']}) + .then(({device}) => Promise.all([ + device.gatt.CALLS([ + getPrimaryService('health_thermometer')| + getPrimaryServices()| + getPrimaryServices('health_thermometer')[UUID]]), + device.gatt.PREVIOUS_CALL])) + .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/script-tests/server/invalid-service-name.js b/testing/web-platform/tests/bluetooth/script-tests/server/invalid-service-name.js new file mode 100644 index 0000000000..52cbb24f4a --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/invalid-service-name.js @@ -0,0 +1,22 @@ +'use strict'; +const test_desc = 'Wrong Service name. Reject with TypeError.'; +const expected = new DOMException( + "Failed to execute 'FUNCTION_NAME' 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.CALLS([ + getPrimaryService('wrong_name')| + getPrimaryServices('wrong_name') + ]), + expected, + 'Wrong Service name passed.')), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/server/no-permission-absent-service.js b/testing/web-platform/tests/bluetooth/script-tests/server/no-permission-absent-service.js new file mode 100644 index 0000000000..200dab3e93 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/no-permission-absent-service.js @@ -0,0 +1,23 @@ +'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.CALLS([ + getPrimaryService(glucose.alias)| + getPrimaryServices(glucose.alias)[UUID] + ]), expected), + assert_promise_rejects_with_message( + device.gatt.FUNCTION_NAME(glucose.name), expected), + assert_promise_rejects_with_message( + device.gatt.FUNCTION_NAME(glucose.uuid), expected)])), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/server/no-permission-for-any-service.js b/testing/web-platform/tests/bluetooth/script-tests/server/no-permission-for-any-service.js new file mode 100644 index 0000000000..60e3ef0080 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/no-permission-for-any-service.js @@ -0,0 +1,17 @@ +'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.CALLS([ + getPrimaryService('heart_rate')| + getPrimaryServices()| + getPrimaryServices('heart_rate')[UUID]]), + expected)), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/server/no-permission-present-service.js b/testing/web-platform/tests/bluetooth/script-tests/server/no-permission-present-service.js new file mode 100644 index 0000000000..3257410685 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/no-permission-present-service.js @@ -0,0 +1,22 @@ +'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.CALLS([ + getPrimaryService(generic_access.alias)| + getPrimaryServices(generic_access.alias)[UUID] + ]), expected), + assert_promise_rejects_with_message( + device.gatt.FUNCTION_NAME(generic_access.name), expected), + assert_promise_rejects_with_message( + device.gatt.FUNCTION_NAME(generic_access.uuid), expected)])), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/server/service-not-found.js b/testing/web-platform/tests/bluetooth/script-tests/server/service-not-found.js new file mode 100644 index 0000000000..0fd2dace78 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/server/service-not-found.js @@ -0,0 +1,16 @@ +'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.CALLS([ + getPrimaryService('glucose')| + getPrimaryServices('glucose')[UUID] + ]), + new DOMException( + `No Services matching UUID ${glucose.uuid} found in Device.`, + 'NotFoundError'))), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/service/blocklisted-characteristic.js b/testing/web-platform/tests/bluetooth/script-tests/service/blocklisted-characteristic.js new file mode 100644 index 0000000000..b26f039a70 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/service/blocklisted-characteristic.js @@ -0,0 +1,19 @@ +'use strict'; +const test_desc = 'Serial Number String characteristic is blocklisted. ' + + 'Should reject with SecurityError.'; +const expected = new DOMException( + 'getCharacteristic(s) called with blocklisted UUID. https://goo.gl/4NeimX', + 'SecurityError'); + +bluetooth_test(() => getHIDDevice({ + filters: [{services: ['device_information']}] +}) + .then(({device}) => device.gatt.getPrimaryService('device_information')) + .then(service => assert_promise_rejects_with_message( + service.CALLS([ + getCharacteristic('serial_number_string')| + getCharacteristics('serial_number_string')[UUID] + ]), + expected, + 'Serial Number String characteristic is blocklisted.')), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/service/characteristic-not-found.js b/testing/web-platform/tests/bluetooth/script-tests/service/characteristic-not-found.js new file mode 100644 index 0000000000..366e046774 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/service/characteristic-not-found.js @@ -0,0 +1,15 @@ +'use strict'; +const test_desc = 'Request for absent characteristics with UUID. ' + + 'Reject with NotFoundError.'; + +bluetooth_test(() => getEmptyHealthThermometerService() + .then(({service}) => assert_promise_rejects_with_message( + service.CALLS([ + getCharacteristic('battery_level')| + getCharacteristics('battery_level')[UUID] + ]), + new DOMException( + `No Characteristics matching UUID ${battery_level.uuid} found ` + + `in Service with UUID ${health_thermometer.uuid}.`, + 'NotFoundError'))), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/service/garbage-collection-ran-during-error.js b/testing/web-platform/tests/bluetooth/script-tests/service/garbage-collection-ran-during-error.js new file mode 100644 index 0000000000..7ed4aaa962 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/service/garbage-collection-ran-during-error.js @@ -0,0 +1,24 @@ +'use strict'; +const test_desc = 'Garbage Collection ran during FUNCTION_NAME ' + + 'call that fails. Should not crash'; +const expected = new DOMException( + 'GATT Server is disconnected. Cannot retrieve characteristics. ' + + '(Re)connect first with `device.gatt.connect`.', + 'NetworkError'); +let promise; + +bluetooth_test(() => getHealthThermometerService() + .then(({service}) => { + promise = assert_promise_rejects_with_message( + service.CALLS([ + getCharacteristic('measurement_interval')| + getCharacteristics()| + getCharacteristics('measurement_interval')[UUID] + ]), expected); + // Disconnect called to clear attributeInstanceMap and allow the object to + // get garbage collected. + service.device.gatt.disconnect(); + }) + .then(garbageCollect) + .then(() => promise), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/service/get-same-object.js b/testing/web-platform/tests/bluetooth/script-tests/service/get-same-object.js new file mode 100644 index 0000000000..db9d740c83 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/service/get-same-object.js @@ -0,0 +1,24 @@ +'use strict'; +const test_desc = 'Calls to FUNCTION_NAME should return the same object.'; + +bluetooth_test(() => getHealthThermometerService() + .then(({service}) => Promise.all([ + service.CALLS([ + getCharacteristic('measurement_interval')| + getCharacteristics()| + getCharacteristics('measurement_interval')[UUID]]), + service.PREVIOUS_CALL])) + .then(([characteristics_first_call, characteristics_second_call]) => { + // Convert to arrays if necessary. + characteristics_first_call = [].concat(characteristics_first_call); + characteristics_second_call = [].concat(characteristics_second_call); + + let first_call_set = new Set(characteristics_first_call); + assert_equals(characteristics_first_call.length, first_call_set.size); + let second_call_set = new Set(characteristics_second_call); + assert_equals(characteristics_second_call.length, second_call_set.size); + + characteristics_first_call.forEach(characteristic => { + assert_true(second_call_set.has(characteristic)); + }); + }), test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/service/invalid-characteristic-name.js b/testing/web-platform/tests/bluetooth/script-tests/service/invalid-characteristic-name.js new file mode 100644 index 0000000000..74cba7ec43 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/service/invalid-characteristic-name.js @@ -0,0 +1,23 @@ +'use strict'; +const test_desc = 'Wrong Characteristic name. Reject with TypeError.'; +const expected = new DOMException( + "Failed to execute 'FUNCTION_NAME' on " + + "'BluetoothRemoteGATTService': Invalid Characteristic 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/characteristics" + + " e.g. 'aerobic_heart_rate_lower_limit'.", + 'TypeError'); + +bluetooth_test(() => getHealthThermometerService() + .then(({service}) => assert_promise_rejects_with_message( + service.CALLS([ + getCharacteristic('wrong_name')| + getCharacteristics('wrong_name') + ]), + expected, + 'Wrong Characteristic name passed.')), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/service/reconnect-during.js b/testing/web-platform/tests/bluetooth/script-tests/service/reconnect-during.js new file mode 100644 index 0000000000..cc71547ac2 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/service/reconnect-during.js @@ -0,0 +1,36 @@ +'use strict'; +const test_desc = 'disconnect() and connect() called during ' + + 'FUNCTION_NAME. Reject with NetworkError.'; +const expected = new DOMException( + 'GATT Server is disconnected. Cannot retrieve characteristics. ' + + '(Re)connect first with `device.gatt.connect`.', + 'NetworkError'); +let device; + +bluetooth_test(() => getHealthThermometerDeviceWithServicesDiscovered({ + filters: [{services: [health_thermometer.name]}], +}) + .then(_ => ({device} = _)) + .then(() => device.gatt.getPrimaryService(health_thermometer.name)) + .then(service => Promise.all([ + // 1. Make a call to service.FUNCTION_NAME, while the service is still + // valid. + assert_promise_rejects_with_message(service.CALLS([ + getCharacteristic(measurement_interval.name)| + getCharacteristics()| + getCharacteristics(measurement_interval.name)[UUID] + ]), expected), + + // 2. disconnect() and connect before the initial call completes. + // This is accomplished by making the calls without waiting for the + // earlier promises to resolve. + // connect() guarantees on OS-level connection, but disconnect() + // only disconnects the current instance. + // getHealthThermometerDeviceWithServicesDiscovered holds another + // connection in an iframe, so disconnect() and connect() are certain to + // reconnect. However, disconnect() will invalidate the service object so + // the subsequent calls made to it will fail, even after reconnecting. + device.gatt.disconnect(), + device.gatt.connect() + ])), + test_desc); diff --git a/testing/web-platform/tests/bluetooth/script-tests/service/service-is-removed.js b/testing/web-platform/tests/bluetooth/script-tests/service/service-is-removed.js new file mode 100644 index 0000000000..aaf0f14436 --- /dev/null +++ b/testing/web-platform/tests/bluetooth/script-tests/service/service-is-removed.js @@ -0,0 +1,20 @@ +'use strict'; +const test_desc = 'Service is removed before FUNCTION_NAME call. ' + + 'Reject with InvalidStateError.'; +const expected = new DOMException('GATT Service no longer exists.', + 'InvalidStateError'); +let service, fake_service, fake_peripheral; + +bluetooth_test(() => getHealthThermometerService() + .then(_ => ({service, fake_service, fake_peripheral} = _)) + .then(() => fake_service.remove()) + .then(() => fake_peripheral.simulateGATTServicesChanged()) + .then(() => assert_promise_rejects_with_message( + service.CALLS([ + getCharacteristic('measurement_interval')| + getCharacteristics()| + getCharacteristics('measurement_interval')[UUID] + ]), + expected, + 'Service got removed.')), + test_desc); |