diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2016-11-04 07:44:42 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-11-04 07:44:42 -0500 |
commit | dae007fd1634bcf1545e67abaa7746fa95f10e94 (patch) | |
tree | af4ee95a6116796e9fb432ad330649f708f50210 /components | |
parent | 73c9847ef81e838c970e44c1645209134fddd32e (diff) | |
parent | ada0256030904412727d85af036dcfd8605dcb97 (diff) | |
download | servo-dae007fd1634bcf1545e67abaa7746fa95f10e94.tar.gz servo-dae007fd1634bcf1545e67abaa7746fa95f10e94.zip |
Auto merge of #13612 - szeged:test-api-impl, r=jdm
WebBluetooth Test API and tests
<!-- Please describe your changes on the following line: -->
This patch depends on the [devices mock device support PR](https://github.com/servo/devices/pull/17).
After it lands, the Cargo files can be updated.
1. Adjust to the changes in [devices mock device support PR](https://github.com/servo/devices/pull/17).
2. WebBluetooth Test API implementation. Based on : https://webbluetoothcg.github.io/web-bluetooth/tests.html
3. Wpt tests for the already landed WebBluetooth functions.
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
<!-- Either: -->
- [x] There are tests for these changes
<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13612)
<!-- Reviewable:end -->
Diffstat (limited to 'components')
-rw-r--r-- | components/bluetooth/Cargo.toml | 3 | ||||
-rw-r--r-- | components/bluetooth/lib.rs | 113 | ||||
-rw-r--r-- | components/bluetooth/test.rs | 501 | ||||
-rw-r--r-- | components/bluetooth_traits/lib.rs | 1 | ||||
-rw-r--r-- | components/script/dom/mod.rs | 1 | ||||
-rw-r--r-- | components/script/dom/testrunner.rs | 53 | ||||
-rw-r--r-- | components/script/dom/webidls/TestRunner.webidl | 16 | ||||
-rw-r--r-- | components/script/dom/webidls/Window.webidl | 7 | ||||
-rw-r--r-- | components/script/dom/window.rs | 8 | ||||
-rw-r--r-- | components/servo/Cargo.lock | 19 |
10 files changed, 703 insertions, 19 deletions
diff --git a/components/bluetooth/Cargo.toml b/components/bluetooth/Cargo.toml index e1ea441e267..7ee5f430751 100644 --- a/components/bluetooth/Cargo.toml +++ b/components/bluetooth/Cargo.toml @@ -12,10 +12,11 @@ path = "lib.rs" [dependencies] bitflags = "0.7" bluetooth_traits = {path = "../bluetooth_traits"} -device = {git = "https://github.com/servo/devices"} +device = {git = "https://github.com/servo/devices", features = ["bluetooth-test"]} ipc-channel = "0.5" rand = "0.3" util = {path = "../util"} +uuid = {version = "0.3.1", features = ["v4"]} [target.'cfg(target_os = "linux")'.dependencies] tinyfiledialogs = {git = "https://github.com/jdm/tinyfiledialogs"} diff --git a/components/bluetooth/lib.rs b/components/bluetooth/lib.rs index 12ba83244be..e3d4eba1a24 100644 --- a/components/bluetooth/lib.rs +++ b/components/bluetooth/lib.rs @@ -11,17 +11,17 @@ extern crate rand; #[cfg(target_os = "linux")] extern crate tinyfiledialogs; extern crate util; +extern crate uuid; + +pub mod test; use bluetooth_traits::{BluetoothCharacteristicMsg, BluetoothCharacteristicsMsg}; use bluetooth_traits::{BluetoothDescriptorMsg, BluetoothDescriptorsMsg}; use bluetooth_traits::{BluetoothDeviceMsg, BluetoothError, BluetoothMethodMsg}; use bluetooth_traits::{BluetoothResult, BluetoothServiceMsg, BluetoothServicesMsg}; use bluetooth_traits::scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence, RequestDeviceoptions}; -use device::bluetooth::BluetoothAdapter; -use device::bluetooth::BluetoothDevice; -use device::bluetooth::BluetoothGATTCharacteristic; -use device::bluetooth::BluetoothGATTDescriptor; -use device::bluetooth::BluetoothGATTService; +use device::bluetooth::{BluetoothAdapter, BluetoothDevice, BluetoothGATTCharacteristic}; +use device::bluetooth::{BluetoothGATTDescriptor, BluetoothGATTService}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use rand::Rng; use std::borrow::ToOwned; @@ -33,6 +33,8 @@ use util::thread::spawn_named; const ADAPTER_ERROR: &'static str = "No adapter found"; +const ADAPTER_NOT_POWERED_ERROR: &'static str = "Bluetooth adapter not powered"; + // A transaction not completed within 30 seconds shall time out. Such a transaction shall be considered to have failed. // https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=286439 (Vol. 3, page 480) const MAXIMUM_TRANSACTION_TIME: u8 = 30; @@ -71,7 +73,12 @@ macro_rules! return_if_cached( macro_rules! get_adapter_or_return_error( ($bl_manager:expr, $sender:expr) => ( match $bl_manager.get_or_create_adapter() { - Some(adapter) => adapter, + Some(adapter) => { + if !adapter.is_powered().unwrap_or(false) { + return drop($sender.send(Err(BluetoothError::Type(ADAPTER_NOT_POWERED_ERROR.to_string())))) + } + adapter + }, None => return drop($sender.send(Err(BluetoothError::Type(ADAPTER_ERROR.to_string())))), } ); @@ -155,6 +162,13 @@ fn matches_filters(device: &BluetoothDevice, filters: &BluetoothScanfilterSequen return filters.iter().any(|f| matches_filter(device, f)) } +fn is_mock_adapter(adapter: &BluetoothAdapter) -> bool { + match adapter { + &BluetoothAdapter::Mock(_) => true, + _ => false, + } +} + pub struct BluetoothManager { receiver: IpcReceiver<BluetoothMethodMsg>, adapter: Option<BluetoothAdapter>, @@ -228,6 +242,9 @@ impl BluetoothManager { BluetoothMethodMsg::WriteValue(id, value, sender) => { self.write_value(id, value, sender) }, + BluetoothMethodMsg::Test(data_set_name, sender) => { + self.test(data_set_name, sender) + } BluetoothMethodMsg::Exit => { break }, @@ -235,13 +252,46 @@ impl BluetoothManager { } } + // Test + + fn test(&mut self, data_set_name: String, sender: IpcSender<BluetoothResult<()>>) { + self.address_to_id.clear(); + self.service_to_device.clear(); + self.characteristic_to_service.clear(); + self.descriptor_to_characteristic.clear(); + self.cached_devices.clear(); + self.cached_services.clear(); + self.cached_characteristics.clear(); + self.cached_descriptors.clear(); + self.allowed_services.clear(); + self.adapter = BluetoothAdapter::init_mock().ok(); + match test::test(self, data_set_name) { + Ok(_) => { + let _ = sender.send(Ok(())); + }, + Err(error) => { + let _ = sender.send(Err(BluetoothError::Type(error.description().to_owned()))); + }, + } + } + // Adapter - fn get_or_create_adapter(&mut self) -> Option<BluetoothAdapter> { + pub fn get_or_create_adapter(&mut self) -> Option<BluetoothAdapter> { let adapter_valid = self.adapter.as_ref().map_or(false, |a| a.get_address().is_ok()); if !adapter_valid { self.adapter = BluetoothAdapter::init().ok(); } + + let adapter = match self.adapter.as_ref() { + Some(adapter) => adapter, + None => return None, + }; + + if is_mock_adapter(adapter) && !adapter.is_present().unwrap_or(false) { + return None; + } + self.adapter.clone() } @@ -270,7 +320,16 @@ impl BluetoothManager { } #[cfg(target_os = "linux")] - fn select_device(&mut self, devices: Vec<BluetoothDevice>) -> Option<String> { + fn select_device(&mut self, devices: Vec<BluetoothDevice>, adapter: &BluetoothAdapter) -> Option<String> { + if is_mock_adapter(adapter) { + for device in devices { + if let Ok(address) = device.get_address() { + return Some(address); + } + } + return None; + } + let mut dialog_rows: Vec<String> = vec!(); for device in devices { dialog_rows.extend_from_slice(&[device.get_address().unwrap_or("".to_string()), @@ -291,7 +350,7 @@ impl BluetoothManager { } #[cfg(not(target_os = "linux"))] - fn select_device(&mut self, devices: Vec<BluetoothDevice>) -> Option<String> { + fn select_device(&mut self, devices: Vec<BluetoothDevice>, _adapter: &BluetoothAdapter) -> Option<String> { for device in devices { if let Ok(address) = device.get_address() { return Some(address); @@ -312,6 +371,17 @@ impl BluetoothManager { device_id } + fn device_from_service_id(&self, service_id: &str) -> Option<BluetoothDevice> { + let device_id = match self.service_to_device.get(service_id) { + Some(id) => id, + None => return None, + }; + match self.cached_devices.get(device_id) { + Some(d) => Some(d.clone()), + None => None, + } + } + // Service fn get_and_cache_gatt_services(&mut self, @@ -464,7 +534,9 @@ impl BluetoothManager { let mut adapter = get_adapter_or_return_error!(self, sender); if let Ok(ref session) = adapter.create_discovery_session() { if session.start_discovery().is_ok() { - thread::sleep(Duration::from_millis(DISCOVERY_TIMEOUT_MS)); + if !is_mock_adapter(&adapter) { + thread::sleep(Duration::from_millis(DISCOVERY_TIMEOUT_MS)); + } } let _ = session.stop_discovery(); } @@ -481,7 +553,7 @@ impl BluetoothManager { } // Step 8. - if let Some(address) = self.select_device(matched_devices) { + if let Some(address) = self.select_device(matched_devices, &adapter) { let device_id = match self.address_to_id.get(&address) { Some(id) => id.clone(), None => return drop(sender.send(Err(BluetoothError::NotFound))), @@ -517,7 +589,12 @@ impl BluetoothManager { for _ in 0..MAXIMUM_TRANSACTION_TIME { match d.is_connected().unwrap_or(false) { true => return drop(sender.send(Ok(true))), - false => thread::sleep(Duration::from_millis(CONNECTION_TIMEOUT_MS)), + false => { + if is_mock_adapter(&adapter) { + break; + } + thread::sleep(Duration::from_millis(CONNECTION_TIMEOUT_MS)); + }, } } return drop(sender.send(Err(BluetoothError::Network))); @@ -617,11 +694,15 @@ impl BluetoothManager { Some(a) => a, None => return drop(sender.send(Err(BluetoothError::Type(ADAPTER_ERROR.to_string())))), }; + let device = match self.device_from_service_id(&service_id) { + Some(device) => device, + None => return drop(sender.send(Err(BluetoothError::NotFound))), + }; let primary_service = match self.get_gatt_service(&mut adapter, &service_id) { Some(s) => s, None => return drop(sender.send(Err(BluetoothError::NotFound))), }; - let services = primary_service.get_includes().unwrap_or(vec!()); + let services = primary_service.get_includes(device).unwrap_or(vec!()); for service in services { if let Ok(service_uuid) = service.get_uuid() { if uuid == service_uuid { @@ -644,11 +725,15 @@ impl BluetoothManager { Some(a) => a, None => return drop(sender.send(Err(BluetoothError::Type(ADAPTER_ERROR.to_string())))), }; + let device = match self.device_from_service_id(&service_id) { + Some(device) => device, + None => return drop(sender.send(Err(BluetoothError::NotFound))), + }; let primary_service = match self.get_gatt_service(&mut adapter, &service_id) { Some(s) => s, None => return drop(sender.send(Err(BluetoothError::NotFound))), }; - let services = primary_service.get_includes().unwrap_or(vec!()); + let services = primary_service.get_includes(device).unwrap_or(vec!()); let mut services_vec = vec!(); for service in services { if let Ok(service_uuid) = service.get_uuid() { diff --git a/components/bluetooth/test.rs b/components/bluetooth/test.rs new file mode 100644 index 00000000000..ed0b296f2ba --- /dev/null +++ b/components/bluetooth/test.rs @@ -0,0 +1,501 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use BluetoothManager; +use device::bluetooth::{BluetoothAdapter, BluetoothDevice}; +use device::bluetooth::{BluetoothGATTCharacteristic, BluetoothGATTDescriptor, BluetoothGATTService}; +use std::borrow::ToOwned; +use std::cell::RefCell; +use std::collections::HashSet; +use std::error::Error; +use std::string::String; +use uuid::Uuid; + +thread_local!(pub static CACHED_IDS: RefCell<HashSet<Uuid>> = RefCell::new(HashSet::new())); + +const ADAPTER_ERROR: &'static str = "No adapter found"; +const WRONG_DATA_SET_ERROR: &'static str = "Wrong data set name was provided"; +const READ_FLAG: &'static str = "read"; +const WRITE_FLAG: &'static str = "write"; + +// Adapter names +// https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=65 +const NOT_PRESENT_ADAPTER: &'static str = "NotPresentAdapter"; +// https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=83 +const NOT_POWERED_ADAPTER: &'static str = "NotPoweredAdapter"; +// https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=118 +const EMPTY_ADAPTER: &'static str = "EmptyAdapter"; +// https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=126 +const GLUCOSE_HEART_RATE_ADAPTER: &'static str = "GlucoseHeartRateAdapter"; +// https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=135 +const UNICODE_DEVICE_ADAPTER: &'static str = "UnicodeDeviceAdapter"; +// https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=205 +const MISSING_SERVICE_HEART_RATE_ADAPTER: &'static str = "MissingServiceHeartRateAdapter"; +// https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=219 +const MISSING_CHARACTERISTIC_HEART_RATE_ADAPTER: &'static str = "MissingCharacteristicHeartRateAdapter"; +const MISSING_DESCRIPTOR_HEART_RATE_ADAPTER: &'static str = "MissingDescriptorHeartRateAdapter"; +// https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=234 +const HEART_RATE_ADAPTER: &'static str = "HeartRateAdapter"; +// https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=250 +const EMPTY_NAME_HEART_RATE_ADAPTER: &'static str = "EmptyNameHeartRateAdapter"; +// https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=267 +const NO_NAME_HEART_RATE_ADAPTER: &'static str = "NoNameHeartRateAdapter"; +// https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=284 +const TWO_HEART_RATE_SERVICES_ADAPTER: &'static str = "TwoHeartRateServicesAdapter"; +const BLACKLIST_TEST_ADAPTER: &'static str = "BlacklistTestAdapter"; + +// Device names +const CONNECTABLE_DEVICE_NAME: &'static str = "Connectable Device"; +const EMPTY_DEVICE_NAME: &'static str = ""; +// https://webbluetoothcg.github.io/web-bluetooth/tests.html#glucosedevice +const GLUCOSE_DEVICE_NAME: &'static str = "Glucose Device"; +// https://webbluetoothcg.github.io/web-bluetooth/tests.html#heartratedevice +const HEART_RATE_DEVICE_NAME: &'static str = "Heart Rate Device"; +const UNICODE_DEVICE_NAME: &'static str = "❤❤❤❤❤❤❤❤❤"; + +// Device addresses +const CONNECTABLE_DEVICE_ADDRESS: &'static str = "00:00:00:00:00:04"; +// https://webbluetoothcg.github.io/web-bluetooth/tests.html#glucosedevice +const GLUCOSE_DEVICE_ADDRESS: &'static str = "00:00:00:00:00:02"; +// https://webbluetoothcg.github.io/web-bluetooth/tests.html#heartratedevice +const HEART_RATE_DEVICE_ADDRESS: &'static str = "00:00:00:00:00:03"; +const UNICODE_DEVICE_ADDRESS: &'static str = "00:00:00:00:00:01"; + +// Service UUIDs +const BLACKLIST_TEST_SERVICE_UUID: &'static str = "611c954a-263b-4f4a-aab6-01ddb953f985"; +// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.device_information.xml +const DEVICE_INFORMATION_UUID: &'static str = "0000180a-0000-1000-8000-00805f9b34fb"; +// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.generic_access.xml +const GENERIC_ACCESS_SERVICE_UUID: &'static str = "00001800-0000-1000-8000-00805f9b34fb"; +// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.glucose.xml +const GLUCOSE_SERVICE_UUID: &'static str = "00001808-0000-1000-8000-00805f9b34fb"; +// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml +const HEART_RATE_SERVICE_UUID: &'static str = "0000180d-0000-1000-8000-00805f9b34fb"; +// https://www.bluetooth.com/specifications/gatt/ +// viewer?attributeXmlFile=org.bluetooth.service.human_interface_device.xml +const HUMAN_INTERFACE_DEVICE_SERVICE_UUID: &'static str = "00001812-0000-1000-8000-00805f9b34fb"; +// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.tx_power.xml +const TX_POWER_SERVICE_UUID: &'static str = "00001804-0000-1000-8000-00805f9b34fb"; + +// Characteristic UUIDs +const BLACKLIST_EXCLUDE_READS_CHARACTERISTIC_UUID: &'static str = "bad1c9a2-9a5b-4015-8b60-1579bbbf2135"; +// https://www.bluetooth.com/specifications/gatt/ +// viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml +const BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID: &'static str = "00002a38-0000-1000-8000-00805f9b34fb"; +// https://www.bluetooth.com/specifications/gatt/ +// viewer?attributeXmlFile=org.bluetooth.characteristic.gap.device_name.xml +const DEVICE_NAME_CHARACTERISTIC_UUID: &'static str = "00002a00-0000-1000-8000-00805f9b34fb"; +// https://www.bluetooth.com/specifications/gatt/ +// viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml +const HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID: &'static str = "00002a37-0000-1000-8000-00805f9b34fb"; +// https://www.bluetooth.com/specifications/gatt/ +// viewer?attributeXmlFile=org.bluetooth.characteristic.gap.peripheral_privacy_flag.xml +const PERIPHERAL_PRIVACY_FLAG_CHARACTERISTIC_UUID: &'static str = "00002a02-0000-1000-8000-00805f9b34fb"; +// https://www.bluetooth.com/specifications/gatt/ +// viewer?attributeXmlFile=org.bluetooth.characteristic.serial_number_string.xml +const SERIAL_NUMBER_STRING_UUID: &'static str = "00002a25-0000-1000-8000-00805f9b34fb"; + +// Descriptor UUIDs +const BLACKLIST_EXCLUDE_READS_DESCRIPTOR_UUID: &'static str = "aaaaaaaa-aaaa-1181-0510-810819516110"; +const BLACKLIST_DESCRIPTOR_UUID: &'static str = "07711111-6104-0970-7011-1107105110aaa"; +// https://www.bluetooth.com/specifications/gatt/ +// viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_user_description.xml +const CHARACTERISTIC_USER_DESCRIPTION_UUID: &'static str = "00002901-0000-1000-8000-00805f9b34fb"; +// https://www.bluetooth.com/specifications/gatt/ +// viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml +const CLIENT_CHARACTERISTIC_CONFIGURATION_UUID: &'static str = "00002902-0000-1000-8000-00805f9b34fb"; +// https://www.bluetooth.com/specifications/gatt/ +// viewer?attributeXmlFile=org.bluetooth.descriptor.number_of_digitals.xml +const NUMBER_OF_DIGITALS_UUID: &'static str = "00002909-0000-1000-8000-00805f9b34fb"; + +const HEART_RATE_DEVICE_NAME_DESCRIPTION: &'static str = "The name of this device."; + +fn generate_id() -> Uuid { + let mut id = Uuid::nil(); + let mut generated = false; + while !generated { + id = Uuid::new_v4(); + CACHED_IDS.with(|cache| + if !cache.borrow().contains(&id) { + cache.borrow_mut().insert(id.clone()); + generated = true; + } + ); + } + id +} + +// Set the adapter's name, is_powered and is_discoverable attributes +fn set_adapter(adapter: &BluetoothAdapter, adapter_name: String) -> Result<(), Box<Error>> { + try!(adapter.set_name(adapter_name)); + try!(adapter.set_powered(true)); + try!(adapter.set_discoverable(true)); + Ok(()) +} + +// Create Device +fn create_device(adapter: &BluetoothAdapter, + name: String, + address: String) + -> Result<BluetoothDevice, Box<Error>> { + let device = try!(BluetoothDevice::create_mock_device(adapter.clone(), generate_id().to_string())); + try!(device.set_name(Some(name))); + try!(device.set_address(address)); + try!(device.set_connectable(true)); + Ok(device) +} + +// Create Device with UUIDs +fn create_device_with_uuids(adapter: &BluetoothAdapter, + name: String, + address: String, + uuids: Vec<String>) + -> Result<BluetoothDevice, Box<Error>> { + let device = try!(create_device(adapter, name, address)); + try!(device.set_uuids(uuids)); + Ok(device) +} + +// Create Service +fn create_service(device: &BluetoothDevice, + uuid: String) + -> Result<BluetoothGATTService, Box<Error>> { + let service = try!(BluetoothGATTService::create_mock_service(device.clone(), generate_id().to_string())); + try!(service.set_uuid(uuid)); + Ok(service) +} + +// Create Characteristic +fn create_characteristic(service: &BluetoothGATTService, + uuid: String) + -> Result<BluetoothGATTCharacteristic, Box<Error>> { + let characteristic = + try!(BluetoothGATTCharacteristic::create_mock_characteristic(service.clone(), generate_id().to_string())); + try!(characteristic.set_uuid(uuid)); + Ok(characteristic) +} + +// Create Characteristic with value +fn create_characteristic_with_value(service: &BluetoothGATTService, + uuid: String, + value: Vec<u8>) + -> Result<BluetoothGATTCharacteristic, Box<Error>> { + let characteristic = try!(create_characteristic(service, uuid)); + try!(characteristic.set_value(value)); + Ok(characteristic) +} + +// Create Descriptor +fn create_descriptor(characteristic: &BluetoothGATTCharacteristic, + uuid: String) + -> Result<BluetoothGATTDescriptor, Box<Error>> { + let descriptor = + try!(BluetoothGATTDescriptor::create_mock_descriptor(characteristic.clone(), generate_id().to_string())); + try!(descriptor.set_uuid(uuid)); + Ok(descriptor) +} + +// Create Descriptor with value +fn create_descriptor_with_value(characteristic: &BluetoothGATTCharacteristic, + uuid: String, + value: Vec<u8>) + -> Result<BluetoothGATTDescriptor, Box<Error>> { + let descriptor = try!(create_descriptor(characteristic, uuid)); + try!(descriptor.set_value(value)); + Ok(descriptor) +} + +fn create_heart_rate_service(device: &BluetoothDevice, + empty: bool) + -> Result<BluetoothGATTService, Box<Error>> { + // Heart Rate Service + let heart_rate_service = try!(create_service(device, HEART_RATE_SERVICE_UUID.to_owned())); + + if empty { + return Ok(heart_rate_service) + } + + // Heart Rate Measurement Characteristic + let _heart_rate_measurement_characteristic = + try!(create_characteristic_with_value(&heart_rate_service, + HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID.to_owned(), + vec![0])); + + // Body Sensor Location Characteristic 1 + let body_sensor_location_characteristic_1 = + try!(create_characteristic_with_value(&heart_rate_service, + BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID.to_owned(), + vec![49])); + try!(body_sensor_location_characteristic_1.set_flags(vec![READ_FLAG.to_string()])); + + // Body Sensor Location Characteristic 2 + let body_sensor_location_characteristic_2 = + try!(create_characteristic_with_value(&heart_rate_service, + BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID.to_owned(), + vec![50])); + try!(body_sensor_location_characteristic_2.set_flags(vec![READ_FLAG.to_string()])); + Ok(heart_rate_service) +} + +fn create_generic_access_service(device: &BluetoothDevice, + empty: bool) + -> Result<BluetoothGATTService, Box<Error>> { + // Generic Access Service + let generic_access_service = + try!(create_service(device, GENERIC_ACCESS_SERVICE_UUID.to_owned())); + + if empty { + return Ok(generic_access_service) + } + + // Device Name Characteristic + let device_name_characteristic = + try!(create_characteristic_with_value(&generic_access_service, + DEVICE_NAME_CHARACTERISTIC_UUID.to_owned(), + HEART_RATE_DEVICE_NAME.as_bytes().to_vec())); + try!(device_name_characteristic.set_flags(vec![READ_FLAG.to_string(), WRITE_FLAG.to_string()])); + + // Number of Digitals descriptor + let number_of_digitals_descriptor_1 = + try!(create_descriptor_with_value(&device_name_characteristic, + NUMBER_OF_DIGITALS_UUID.to_owned(), + vec![49])); + try!(number_of_digitals_descriptor_1.set_flags(vec![READ_FLAG.to_string(), WRITE_FLAG.to_string()])); + + let number_of_digitals_descriptor_2 = + try!(create_descriptor_with_value(&device_name_characteristic, + NUMBER_OF_DIGITALS_UUID.to_owned(), + vec![50])); + try!(number_of_digitals_descriptor_2.set_flags(vec![READ_FLAG.to_string(), WRITE_FLAG.to_string()])); + + // Characteristic User Description Descriptor + let _characteristic_user_description = + try!(create_descriptor_with_value(&device_name_characteristic, + CHARACTERISTIC_USER_DESCRIPTION_UUID.to_owned(), + HEART_RATE_DEVICE_NAME_DESCRIPTION.as_bytes().to_vec())); + + // Client Characteristic Configuration descriptor + let _client_characteristic_configuration = + try!(create_descriptor_with_value(&device_name_characteristic, + CLIENT_CHARACTERISTIC_CONFIGURATION_UUID.to_owned(), + vec![0])); + + // Peripheral Privacy Flag Characteristic + let peripheral_privacy_flag_characteristic = + try!(create_characteristic(&generic_access_service, PERIPHERAL_PRIVACY_FLAG_CHARACTERISTIC_UUID.to_owned())); + try!(peripheral_privacy_flag_characteristic + .set_flags(vec![READ_FLAG.to_string(), WRITE_FLAG.to_string()])); + Ok(generic_access_service) +} + +// Create Heart Rate Device +fn create_heart_rate_device(adapter: &BluetoothAdapter, + empty: bool) + -> Result<BluetoothDevice, Box<Error>> { + // Heart Rate Device + let heart_rate_device = + try!(create_device_with_uuids(adapter, + HEART_RATE_DEVICE_NAME.to_owned(), + HEART_RATE_DEVICE_ADDRESS.to_owned(), + vec![GENERIC_ACCESS_SERVICE_UUID.to_owned(), + HEART_RATE_SERVICE_UUID.to_owned()])); + + if empty { + return Ok(heart_rate_device); + } + + // Generic Access Service + let _generic_access_service = try!(create_generic_access_service(&heart_rate_device, false)); + + // Heart Rate Service + let _heart_rate_service = try!(create_heart_rate_service(&heart_rate_device, false)); + + Ok(heart_rate_device) +} + +fn create_missing_characterisitc_heart_rate_device(adapter: &BluetoothAdapter) -> Result<(), Box<Error>> { + let heart_rate_device_empty = try!(create_heart_rate_device(adapter, true)); + + let _generic_access_service_empty = try!(create_generic_access_service(&heart_rate_device_empty, true)); + + let _heart_rate_service_empty = try!(create_heart_rate_service(&heart_rate_device_empty, true)); + + Ok(()) +} + +fn create_missing_descriptor_heart_rate_device(adapter: &BluetoothAdapter) -> Result<(), Box<Error>> { + let heart_rate_device_empty = try!(create_heart_rate_device(adapter, true)); + + let generic_access_service_empty = try!(create_generic_access_service(&heart_rate_device_empty, true)); + + let _device_name_characteristic = + try!(create_characteristic_with_value(&generic_access_service_empty, + DEVICE_NAME_CHARACTERISTIC_UUID.to_owned(), + HEART_RATE_DEVICE_NAME.as_bytes().to_vec())); + + let peripheral_privacy_flag_characteristic = + try!(create_characteristic(&generic_access_service_empty, + PERIPHERAL_PRIVACY_FLAG_CHARACTERISTIC_UUID.to_owned())); + try!(peripheral_privacy_flag_characteristic.set_flags(vec![READ_FLAG.to_string(), WRITE_FLAG.to_string()])); + + let _heart_rate_service = try!(create_heart_rate_service(&heart_rate_device_empty, false)); + + Ok(()) +} + +fn create_two_heart_rate_services_device(adapter: &BluetoothAdapter) -> Result<(), Box<Error>> { + let heart_rate_device_empty = try!(create_heart_rate_device(adapter, true)); + + try!(heart_rate_device_empty.set_uuids(vec![GENERIC_ACCESS_SERVICE_UUID.to_owned(), + HEART_RATE_SERVICE_UUID.to_owned(), + HEART_RATE_SERVICE_UUID.to_owned()])); + + let _generic_access_service = try!(create_generic_access_service(&heart_rate_device_empty, false)); + + let heart_rate_service_empty_1 = try!(create_heart_rate_service(&heart_rate_device_empty, true)); + + let heart_rate_service_empty_2 = try!(create_heart_rate_service(&heart_rate_device_empty, true)); + + let _heart_rate_measurement_characteristic = + try!(create_characteristic_with_value(&heart_rate_service_empty_1, + HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID.to_owned(), + vec![0])); + + let _body_sensor_location_characteristic_1 = + try!(create_characteristic_with_value(&heart_rate_service_empty_1, + BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID.to_owned(), + vec![49])); + + let _body_sensor_location_characteristic_2 = + try!(create_characteristic_with_value(&heart_rate_service_empty_2, + BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID.to_owned(), + vec![50])); + Ok(()) +} + +fn create_blacklisted_device(adapter: &BluetoothAdapter) -> Result<(), Box<Error>> { + let connectable_device = + try!(create_device_with_uuids(adapter, + CONNECTABLE_DEVICE_NAME.to_owned(), + CONNECTABLE_DEVICE_ADDRESS.to_owned(), + vec![BLACKLIST_TEST_SERVICE_UUID.to_owned(), + DEVICE_INFORMATION_UUID.to_owned(), + GENERIC_ACCESS_SERVICE_UUID.to_owned(), + HEART_RATE_SERVICE_UUID.to_owned(), + HUMAN_INTERFACE_DEVICE_SERVICE_UUID.to_owned()])); + + let blacklist_test_service = try!(create_service(&connectable_device, BLACKLIST_TEST_SERVICE_UUID.to_owned())); + + let blacklist_exclude_reads_characteristic = + try!(create_characteristic(&blacklist_test_service, + BLACKLIST_EXCLUDE_READS_CHARACTERISTIC_UUID.to_owned())); + try!(blacklist_exclude_reads_characteristic + .set_flags(vec![READ_FLAG.to_string(), WRITE_FLAG.to_string()])); + + let _blacklist_exclude_reads_descriptor = + try!(create_descriptor_with_value(&blacklist_exclude_reads_characteristic, + BLACKLIST_EXCLUDE_READS_DESCRIPTOR_UUID.to_owned(), + vec![54; 3])); + + let _blacklist_descriptor = + try!(create_descriptor_with_value(&blacklist_exclude_reads_characteristic, + BLACKLIST_DESCRIPTOR_UUID.to_owned(), + vec![54; 3])); + + let device_information_service = try!(create_service(&connectable_device, DEVICE_INFORMATION_UUID.to_owned())); + + let _serial_number_string_characteristic = + try!(create_characteristic(&device_information_service, SERIAL_NUMBER_STRING_UUID.to_owned())); + + let _generic_access_service = try!(create_generic_access_service(&connectable_device, false)); + + let _heart_rate_service = try!(create_heart_rate_service(&connectable_device, false)); + + let _human_interface_device_service = + try!(create_service(&connectable_device, HUMAN_INTERFACE_DEVICE_SERVICE_UUID.to_owned())); + Ok(()) +} + +pub fn test(manager: &mut BluetoothManager, data_set_name: String) -> Result<(), Box<Error>> { + let may_existing_adapter = manager.get_or_create_adapter(); + let adapter = match may_existing_adapter.as_ref() { + Some(adapter) => adapter, + None => return Err(Box::from(ADAPTER_ERROR.to_string())), + }; + match data_set_name.as_str() { + NOT_PRESENT_ADAPTER => { + try!(set_adapter(adapter, NOT_PRESENT_ADAPTER.to_owned())); + try!(adapter.set_present(false)); + }, + NOT_POWERED_ADAPTER => { + try!(set_adapter(adapter, NOT_POWERED_ADAPTER.to_owned())); + try!(adapter.set_powered(false)); + }, + EMPTY_ADAPTER => { + try!(set_adapter(adapter, EMPTY_ADAPTER.to_owned())); + }, + GLUCOSE_HEART_RATE_ADAPTER => { + try!(set_adapter(adapter, GLUCOSE_HEART_RATE_ADAPTER.to_owned())); + + let _glucose_devie = try!(create_device_with_uuids(adapter, + GLUCOSE_DEVICE_NAME.to_owned(), + GLUCOSE_DEVICE_ADDRESS.to_owned(), + vec![GLUCOSE_SERVICE_UUID.to_owned(), + TX_POWER_SERVICE_UUID.to_owned()])); + + let _heart_rate_device_empty = try!(create_heart_rate_device(adapter, true)); + }, + UNICODE_DEVICE_ADAPTER => { + try!(set_adapter(adapter, UNICODE_DEVICE_ADAPTER.to_owned())); + + let _unicode_device = try!(create_device(adapter, + UNICODE_DEVICE_NAME.to_owned(), + UNICODE_DEVICE_ADDRESS.to_owned())); + }, + MISSING_SERVICE_HEART_RATE_ADAPTER => { + try!(set_adapter(adapter, MISSING_SERVICE_HEART_RATE_ADAPTER.to_owned())); + + let _heart_rate_device_empty = try!(create_heart_rate_device(adapter, true)); + }, + MISSING_CHARACTERISTIC_HEART_RATE_ADAPTER => { + try!(set_adapter(adapter, MISSING_CHARACTERISTIC_HEART_RATE_ADAPTER.to_owned())); + + let _ = try!(create_missing_characterisitc_heart_rate_device(adapter)); + }, + MISSING_DESCRIPTOR_HEART_RATE_ADAPTER => { + try!(set_adapter(adapter, MISSING_DESCRIPTOR_HEART_RATE_ADAPTER.to_owned())); + + let _ = try!(create_missing_descriptor_heart_rate_device(adapter)); + }, + HEART_RATE_ADAPTER => { + try!(set_adapter(adapter, HEART_RATE_ADAPTER.to_owned())); + + let _heart_rate_device = try!(create_heart_rate_device(adapter, false)); + }, + EMPTY_NAME_HEART_RATE_ADAPTER => { + try!(set_adapter(adapter, EMPTY_NAME_HEART_RATE_ADAPTER.to_owned())); + + let heart_rate_device = try!(create_heart_rate_device(adapter, false)); + try!(heart_rate_device.set_name(Some(EMPTY_DEVICE_NAME.to_owned()))); + }, + NO_NAME_HEART_RATE_ADAPTER => { + try!(set_adapter(adapter, NO_NAME_HEART_RATE_ADAPTER.to_owned())); + + let heart_rate_device = try!(create_heart_rate_device(adapter, false)); + try!(heart_rate_device.set_name(None)); + }, + TWO_HEART_RATE_SERVICES_ADAPTER => { + try!(set_adapter(adapter, TWO_HEART_RATE_SERVICES_ADAPTER.to_owned())); + + let _ = try!(create_two_heart_rate_services_device(adapter)); + }, + BLACKLIST_TEST_ADAPTER => { + try!(set_adapter(adapter, BLACKLIST_TEST_ADAPTER.to_owned())); + + let _ = try!(create_blacklisted_device(adapter)); + }, + _ => return Err(Box::from(WRONG_DATA_SET_ERROR.to_string())), + } + return Ok(()); +} diff --git a/components/bluetooth_traits/lib.rs b/components/bluetooth_traits/lib.rs index dcff8cead91..a1b1b41be6e 100644 --- a/components/bluetooth_traits/lib.rs +++ b/components/bluetooth_traits/lib.rs @@ -86,5 +86,6 @@ pub enum BluetoothMethodMsg { GetDescriptors(String, Option<String>, IpcSender<BluetoothResult<BluetoothDescriptorsMsg>>), ReadValue(String, IpcSender<BluetoothResult<Vec<u8>>>), WriteValue(String, Vec<u8>, IpcSender<BluetoothResult<bool>>), + Test(String, IpcSender<BluetoothResult<()>>), Exit, } diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 14498823424..d419d41ec61 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -399,6 +399,7 @@ pub mod testbinding; pub mod testbindingiterable; pub mod testbindingpairiterable; pub mod testbindingproxy; +pub mod testrunner; pub mod text; pub mod textdecoder; pub mod textencoder; diff --git a/components/script/dom/testrunner.rs b/components/script/dom/testrunner.rs new file mode 100644 index 00000000000..f96ab6dbf65 --- /dev/null +++ b/components/script/dom/testrunner.rs @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use bluetooth_traits::BluetoothMethodMsg; +use dom::bindings::codegen::Bindings::TestRunnerBinding; +use dom::bindings::codegen::Bindings::TestRunnerBinding::TestRunnerMethods; +use dom::bindings::error::{Error, ErrorResult}; +use dom::bindings::js::Root; +use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; +use dom::bindings::str::DOMString; +use dom::globalscope::GlobalScope; +use ipc_channel::ipc::{self, IpcSender}; + +// https://webbluetoothcg.github.io/web-bluetooth/tests#test-runner + #[dom_struct] +pub struct TestRunner { + reflector_: Reflector, +} + +impl TestRunner { + pub fn new_inherited() -> TestRunner { + TestRunner { + reflector_: Reflector::new(), + } + } + + pub fn new(global: &GlobalScope) -> Root<TestRunner> { + reflect_dom_object(box TestRunner::new_inherited(), + global, + TestRunnerBinding::Wrap) + } + + fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> { + self.global().as_window().bluetooth_thread() + } +} + +impl TestRunnerMethods for TestRunner { + // https://webbluetoothcg.github.io/web-bluetooth/tests#setBluetoothMockDataSet + fn SetBluetoothMockDataSet(&self, dataSetName: DOMString) -> ErrorResult { + let (sender, receiver) = ipc::channel().unwrap(); + self.get_bluetooth_thread().send(BluetoothMethodMsg::Test(String::from(dataSetName), sender)).unwrap(); + match receiver.recv().unwrap().into() { + Ok(()) => { + Ok(()) + }, + Err(error) => { + Err(Error::from(error)) + }, + } + } +} diff --git a/components/script/dom/webidls/TestRunner.webidl b/components/script/dom/webidls/TestRunner.webidl new file mode 100644 index 00000000000..0326c14dbec --- /dev/null +++ b/components/script/dom/webidls/TestRunner.webidl @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// https://webbluetoothcg.github.io/web-bluetooth/tests#test-runner + +// callback BluetoothManualChooserEventsCallback = void(sequence<DOMString> events); + +[Pref="dom.bluetooth.testing.enabled", Exposed=Window] +interface TestRunner { + [Throws] + void setBluetoothMockDataSet(DOMString dataSetName); + // void setBluetoothManualChooser(); + // void getBluetoothManualChooserEvents(BluetoothManualChooserEventsCallback callback); + // void sendBluetoothManualChooserEvent(DOMString event, DOMString argument); +}; diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl index ad3494b6f5f..5e7e08d4b18 100644 --- a/components/script/dom/webidls/Window.webidl +++ b/components/script/dom/webidls/Window.webidl @@ -185,3 +185,10 @@ Window implements WindowLocalStorage; // http://w3c.github.io/animation-timing/#framerequestcallback callback FrameRequestCallback = void (DOMHighResTimeStamp time); + +// https://webbluetoothcg.github.io/web-bluetooth/tests#test-interfaces +partial interface Window { + [Pref="dom.bluetooth.testing.enabled", Exposed=Window] + readonly attribute TestRunner testRunner; + //readonly attribute EventSender eventSender; +}; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index c77ed80cff9..3d3d7dde2a7 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -45,6 +45,7 @@ use dom::performance::Performance; use dom::promise::Promise; use dom::screen::Screen; use dom::storage::Storage; +use dom::testrunner::TestRunner; use euclid::{Point2D, Rect, Size2D}; use fetch; use ipc_channel::ipc::{self, IpcSender}; @@ -239,6 +240,8 @@ pub struct Window { /// All the MediaQueryLists we need to update media_query_lists: WeakMediaQueryListVec, + + test_runner: MutNullableHeap<JS<TestRunner>>, } impl Window { @@ -881,6 +884,10 @@ impl WindowMethods for Window { fn Fetch(&self, input: RequestOrUSVString, init: &RequestInit) -> Rc<Promise> { fetch::Fetch(&self.upcast(), input, init) } + + fn TestRunner(&self) -> Root<TestRunner> { + self.test_runner.or_init(|| TestRunner::new(self.upcast())) + } } impl Window { @@ -1588,6 +1595,7 @@ impl Window { error_reporter: error_reporter, scroll_offsets: DOMRefCell::new(HashMap::new()), media_query_lists: WeakMediaQueryListVec::new(), + test_runner: Default::default(), }; WindowBinding::Wrap(runtime.cx(), win) diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index c3263e06541..3ae065e88eb 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -199,6 +199,7 @@ dependencies = [ "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "tinyfiledialogs 0.1.0 (git+https://github.com/jdm/tinyfiledialogs)", "util 0.0.1", + "uuid 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -216,8 +217,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "blurmock" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "blurz" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dbus 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -515,10 +524,11 @@ dependencies = [ [[package]] name = "device" version = "0.0.1" -source = "git+https://github.com/servo/devices#6d40b1412fb496b0d9434ee2f46e9dfc4dc67ae7" +source = "git+https://github.com/servo/devices#4a6ab4be0de229fafa6aa3657a5702646832ba08" dependencies = [ "blurdroid 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "blurz 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blurmock 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "blurz 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2905,7 +2915,8 @@ dependencies = [ "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" "checksum blurdroid 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5fce4ea3366b583e9d49e1aa3a42252e53b42911bccd06f31c3e81c48ccfc79e" -"checksum blurz 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96134f6ac62fa6925761dbdb4096617d65d7c1d383d90e5c2d4c489919f773dc" +"checksum blurmock 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c3034c7372bc7951e0a916b7e952b0043cd4ccb5112cd30827f0e1708e05c2b1" +"checksum blurz 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d49796c8d5a1b5f6b2b8686e46ed4ab842987c477f765b69f1d3e8df6072608" "checksum brotli 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bff2d5511b5ba5840f46cc3f9c0c3ab09db20e9b9a4db344ef7df3fb547a627a" "checksum browserhtml 0.1.17 (git+https://github.com/browserhtml/browserhtml?branch=crate)" = "<none>" "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" |