aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2016-11-04 07:44:42 -0500
committerGitHub <noreply@github.com>2016-11-04 07:44:42 -0500
commitdae007fd1634bcf1545e67abaa7746fa95f10e94 (patch)
treeaf4ee95a6116796e9fb432ad330649f708f50210 /components
parent73c9847ef81e838c970e44c1645209134fddd32e (diff)
parentada0256030904412727d85af036dcfd8605dcb97 (diff)
downloadservo-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.toml3
-rw-r--r--components/bluetooth/lib.rs113
-rw-r--r--components/bluetooth/test.rs501
-rw-r--r--components/bluetooth_traits/lib.rs1
-rw-r--r--components/script/dom/mod.rs1
-rw-r--r--components/script/dom/testrunner.rs53
-rw-r--r--components/script/dom/webidls/TestRunner.webidl16
-rw-r--r--components/script/dom/webidls/Window.webidl7
-rw-r--r--components/script/dom/window.rs8
-rw-r--r--components/servo/Cargo.lock19
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"