BluetoothUUID refactor

This commit is contained in:
Zakor Gyula 2016-11-21 16:07:59 +01:00
parent 61a225bab0
commit 7e2a01210f
11 changed files with 82 additions and 57 deletions

View file

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::UnionTypes::StringOrUnsignedLong; use dom::bindings::codegen::UnionTypes::StringOrUnsignedLong;
use dom::bindings::error::Error::Syntax; use dom::bindings::error::Error::Type;
use dom::bindings::error::Fallible; use dom::bindings::error::Fallible;
use dom::bindings::reflector::Reflector; use dom::bindings::reflector::Reflector;
use dom::bindings::str::DOMString; use dom::bindings::str::DOMString;
@ -268,6 +268,18 @@ const SERVICE_PREFIX: &'static str = "org.bluetooth.service";
const CHARACTERISTIC_PREFIX: &'static str = "org.bluetooth.characteristic"; const CHARACTERISTIC_PREFIX: &'static str = "org.bluetooth.characteristic";
const DESCRIPTOR_PREFIX: &'static str = "org.bluetooth.descriptor"; const DESCRIPTOR_PREFIX: &'static str = "org.bluetooth.descriptor";
const VALID_UUID_REGEX: &'static str = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; const VALID_UUID_REGEX: &'static str = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
// https://cs.chromium.org/chromium/src/third_party/WebKit/Source/modules/bluetooth/BluetoothUUID.cpp?l=314
const UUID_ERROR_MESSAGE: &'static str = "It must be a valid UUID alias (e.g. 0x1234), \
UUID (lowercase hex characters e.g. '00001234-0000-1000-8000-00805f9b34fb'),\nor recognized standard name from";
// https://cs.chromium.org/chromium/src/third_party/WebKit/Source/modules/bluetooth/BluetoothUUID.cpp?l=321
const SERVICES_ERROR_MESSAGE: &'static str = "https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx\
\ne.g. 'alert_notification'.";
// https://cs.chromium.org/chromium/src/third_party/WebKit/Source/modules/bluetooth/BluetoothUUID.cpp?l=327
const CHARACTERISTIC_ERROR_MESSAGE: &'static str = "https://developer.bluetooth.org/gatt/characteristics/Pages/\
CharacteristicsHome.aspx\ne.g. 'aerobic_heart_rate_lower_limit'.";
// https://cs.chromium.org/chromium/src/third_party/WebKit/Source/modules/bluetooth/BluetoothUUID.cpp?l=333
const DESCRIPTOR_ERROR_MESSAGE: &'static str = "https://developer.bluetooth.org/gatt/descriptors/Pages/\
DescriptorsHomePage.aspx\ne.g. 'gatt.characteristic_presentation_format'.";
impl BluetoothUUID { impl BluetoothUUID {
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothuuid-canonicaluuid // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothuuid-canonicaluuid
@ -325,22 +337,35 @@ fn resolve_uuid_name(
prefix: &str) prefix: &str)
-> Fallible<DOMString> { -> Fallible<DOMString> {
match name { match name {
// Step 1 // Step 1.
StringOrUnsignedLong::UnsignedLong(unsigned32) => { StringOrUnsignedLong::UnsignedLong(unsigned32) => {
Ok(canonical_uuid(unsigned32)) Ok(canonical_uuid(unsigned32))
}, },
StringOrUnsignedLong::String(dstring) => { StringOrUnsignedLong::String(dstring) => {
// Step 2 // Step 2.
let regex = Regex::new(VALID_UUID_REGEX).unwrap(); let regex = Regex::new(VALID_UUID_REGEX).unwrap();
if regex.is_match(&*dstring) { if regex.is_match(&*dstring) {
Ok(dstring) Ok(dstring)
} else { } else {
// Step 3 // Step 3.
let concatenated = format!("{}.{}", prefix, dstring); let concatenated = format!("{}.{}", prefix, dstring);
let is_in_table = assigned_numbers_table.iter().find(|p| p.0 == concatenated); let is_in_table = assigned_numbers_table.iter().find(|p| p.0 == concatenated);
match is_in_table { match is_in_table {
Some(&(_, alias)) => Ok(canonical_uuid(alias)), Some(&(_, alias)) => Ok(canonical_uuid(alias)),
None => Err(Syntax), None => {
let (attribute_type, error_url_message) = match prefix {
SERVICE_PREFIX => ("Service", SERVICES_ERROR_MESSAGE),
CHARACTERISTIC_PREFIX => ("Characteristic", CHARACTERISTIC_ERROR_MESSAGE),
DESCRIPTOR_PREFIX => ("Descriptor", DESCRIPTOR_ERROR_MESSAGE),
_ => unreachable!(),
};
// Step 4.
return Err(Type(format!("Invalid {} name : '{}'.\n{} {}",
attribute_type,
dstring,
UUID_ERROR_MESSAGE,
error_url_message)));
},
} }
} }
}, },

View file

@ -12,6 +12,6 @@ promise_test(t => {
}) })
.then(device => device.gatt.connect()) .then(device => device.gatt.connect())
.then(gattServer => gattServer.getPrimaryService(generic_access.name)) .then(gattServer => gattServer.getPrimaryService(generic_access.name))
.then(service => promise_rejects(t, 'SyntaxError', service.getCharacteristic(wrong.name))); .then(service => promise_rejects(t, new TypeError(), service.getCharacteristic(wrong.name)));
}, 'Wrong Characteristic name. Reject with SyntaxError.'); }, 'Wrong Characteristic name. Reject with TypeError.');
</script> </script>

View file

@ -12,6 +12,6 @@ promise_test(t => {
}) })
.then(device => device.gatt.connect()) .then(device => device.gatt.connect())
.then(gattServer => gattServer.getPrimaryService(generic_access.name)) .then(gattServer => gattServer.getPrimaryService(generic_access.name))
.then(service => promise_rejects(t, 'SyntaxError', service.getCharacteristics(wrong.name))); .then(service => promise_rejects(t, new TypeError(), service.getCharacteristics(wrong.name)));
}, 'Invalid Characteristic name. Reject with SyntaxError.'); }, 'Invalid Characteristic name. Reject with TypeError.');
</script> </script>

View file

@ -13,6 +13,6 @@ promise_test(t => {
.then(device => device.gatt.connect()) .then(device => device.gatt.connect())
.then(gattServer => gattServer.getPrimaryService(generic_access.name)) .then(gattServer => gattServer.getPrimaryService(generic_access.name))
.then(service => service.getCharacteristic(device_name.name)) .then(service => service.getCharacteristic(device_name.name))
.then(characteristic => promise_rejects(t, 'SyntaxError', characteristic.getDescriptor(wrong.name))); .then(characteristic => promise_rejects(t, new TypeError(), characteristic.getDescriptor(wrong.name)));
}, 'Wrong descriptor name. Reject with SyntaxError.'); }, 'Wrong descriptor name. Reject with TypeError.');
</script> </script>

View file

@ -13,6 +13,6 @@ promise_test(t => {
.then(device => device.gatt.connect()) .then(device => device.gatt.connect())
.then(gattServer => gattServer.getPrimaryService(generic_access.name)) .then(gattServer => gattServer.getPrimaryService(generic_access.name))
.then(service => service.getCharacteristic(device_name.name)) .then(service => service.getCharacteristic(device_name.name))
.then(characteristic => promise_rejects(t, 'SyntaxError', characteristic.getDescriptors(wrong.name))); .then(characteristic => promise_rejects(t, new TypeError(), characteristic.getDescriptors(wrong.name)));
}, 'Wrong descriptor name. Reject with SyntaxError.'); }, 'Wrong descriptor name. Reject with TypeError.');
</script> </script>

View file

@ -10,6 +10,6 @@ promise_test(t => {
filters: [{services: [heart_rate.name]}] filters: [{services: [heart_rate.name]}]
}) })
.then(device => device.gatt.connect()) .then(device => device.gatt.connect())
.then(gattServer => promise_rejects(t, 'SyntaxError', gattServer.getPrimaryService(wrong.name))); .then(gattServer => promise_rejects(t, new TypeError(), gattServer.getPrimaryService(wrong.name)));
}, 'Wrong Service name. Reject with SyntaxError.'); }, 'Wrong Service name. Reject with TypeError.');
</script> </script>

View file

@ -10,6 +10,6 @@ promise_test(t => {
filters: [{services: [heart_rate.name]}] filters: [{services: [heart_rate.name]}]
}) })
.then(device => device.gatt.connect()) .then(device => device.gatt.connect())
.then(gattServer => promise_rejects(t, 'SyntaxError', gattServer.getPrimaryServices(wrong.name))); .then(gattServer => promise_rejects(t, new TypeError(), gattServer.getPrimaryServices(wrong.name)));
}, 'Wrong Service name. Reject with SyntaxError.'); }, 'Wrong Service name. Reject with TypeError.');
</script> </script>

View file

@ -83,23 +83,23 @@ test(() => {
test(() => { test(() => {
let all_caps_uuid = '1A2B3C4D-5E6F-7A8B-9C0D-1E2F3A4B5C6D'; let all_caps_uuid = '1A2B3C4D-5E6F-7A8B-9C0D-1E2F3A4B5C6D';
assert_throws('SyntaxError', () => BluetoothUUID.getService(all_caps_uuid)); assert_throws(new TypeError(), () => BluetoothUUID.getService(all_caps_uuid));
assert_throws('SyntaxError', () => BluetoothUUID.getCharacteristic(all_caps_uuid)); assert_throws(new TypeError(), () => BluetoothUUID.getCharacteristic(all_caps_uuid));
assert_throws('SyntaxError', () => BluetoothUUID.getDescriptor(all_caps_uuid)); assert_throws(new TypeError(), () => BluetoothUUID.getDescriptor(all_caps_uuid));
}, 'A UUID String with uppercase letters is an invalid UUID.'); }, 'A UUID String with uppercase letters is an invalid UUID.');
test(() => { test(() => {
let string_alias = 'deadbeef'; let string_alias = 'deadbeef';
assert_throws('SyntaxError', () => BluetoothUUID.getService(string_alias)); assert_throws(new TypeError(), () => BluetoothUUID.getService(string_alias));
assert_throws('SyntaxError', () => BluetoothUUID.getCharacteristic(string_alias)); assert_throws(new TypeError(), () => BluetoothUUID.getCharacteristic(string_alias));
assert_throws('SyntaxError', () => BluetoothUUID.getDescriptor(string_alias)); assert_throws(new TypeError(), () => BluetoothUUID.getDescriptor(string_alias));
}, 'A 32bit *String* alias is invalid.'); }, 'A 32bit *String* alias is invalid.');
test(() => { test(() => {
let invalid_character_uuid = '0000000g-0000-1000-8000-00805f9b34fb'; let invalid_character_uuid = '0000000g-0000-1000-8000-00805f9b34fb';
assert_throws('SyntaxError', () => BluetoothUUID.getService(invalid_character_uuid)); assert_throws(new TypeError(), () => BluetoothUUID.getService(invalid_character_uuid));
assert_throws('SyntaxError', () => BluetoothUUID.getCharacteristic(invalid_character_uuid)); assert_throws(new TypeError(), () => BluetoothUUID.getCharacteristic(invalid_character_uuid));
assert_throws('SyntaxError', () => BluetoothUUID.getDescriptor(invalid_character_uuid)); assert_throws(new TypeError(), () => BluetoothUUID.getDescriptor(invalid_character_uuid));
}, 'A UUID with invalid characters is an invalid UUID.'); }, 'A UUID with invalid characters is an invalid UUID.');
test(() => { test(() => {
@ -112,31 +112,31 @@ test(() => {
}, 'A valid UUID from a name.'); }, 'A valid UUID from a name.');
test(() => { test(() => {
assert_throws('SyntaxError', () => { assert_throws(new TypeError(), () => {
BluetoothUUID.getService(aerobic_heart_rate_lower_limit.name); BluetoothUUID.getService(aerobic_heart_rate_lower_limit.name);
}); });
assert_throws('SyntaxError', () => { assert_throws(new TypeError(), () => {
BluetoothUUID.getService(characteristic_extended_properties.name); BluetoothUUID.getService(characteristic_extended_properties.name);
}); });
assert_throws('SyntaxError', () => { assert_throws(new TypeError(), () => {
BluetoothUUID.getCharacteristic(alert_notification.name); BluetoothUUID.getCharacteristic(alert_notification.name);
}); });
assert_throws('SyntaxError', () => { assert_throws(new TypeError(), () => {
BluetoothUUID.getCharacteristic(characteristic_extended_properties.name); BluetoothUUID.getCharacteristic(characteristic_extended_properties.name);
}); });
assert_throws('SyntaxError', () => { assert_throws(new TypeError(), () => {
BluetoothUUID.getDescriptor(alert_notification.name); BluetoothUUID.getDescriptor(alert_notification.name);
}); });
assert_throws('SyntaxError', () => { assert_throws(new TypeError(), () => {
BluetoothUUID.getDescriptor(aerobic_heart_rate_lower_limit.name); BluetoothUUID.getDescriptor(aerobic_heart_rate_lower_limit.name);
}); });
}, 'Make sure attributes don\'t share a map'); }, 'Make sure attributes don\'t share a map');
test(() => { test(() => {
let wrong_name = 'wrong_name'; let wrong_name = 'wrong_name';
assert_throws('SyntaxError', () => BluetoothUUID.getService(wrong_name)); assert_throws(new TypeError(), () => BluetoothUUID.getService(wrong_name));
assert_throws('SyntaxError', () => BluetoothUUID.getCharacteristic(wrong_name)); assert_throws(new TypeError(), () => BluetoothUUID.getCharacteristic(wrong_name));
assert_throws('SyntaxError', () => BluetoothUUID.getDescriptor(wrong_name)); assert_throws(new TypeError(), () => BluetoothUUID.getDescriptor(wrong_name));
}, 'Invalid Descriptor name'); }, 'Invalid Descriptor name');
test(() => { test(() => {
@ -154,25 +154,25 @@ test(() => {
assert_equals(BluetoothUUID.canonicalUUID(true), BluetoothUUID.canonicalUUID(1)); assert_equals(BluetoothUUID.canonicalUUID(true), BluetoothUUID.canonicalUUID(1));
assert_throws(new TypeError, () => BluetoothUUID.canonicalUUID(NaN)); assert_throws(new TypeError, () => BluetoothUUID.canonicalUUID(NaN));
// getService // getService
assert_throws('SyntaxError', () => BluetoothUUID.getService(object)); assert_throws(new TypeError(), () => BluetoothUUID.getService(object));
assert_throws('SyntaxError', () => BluetoothUUID.getService(array)); assert_throws(new TypeError(), () => BluetoothUUID.getService(array));
assert_throws('SyntaxError', () => BluetoothUUID.getService(func)); assert_throws(new TypeError(), () => BluetoothUUID.getService(func));
assert_throws('SyntaxError', () => BluetoothUUID.getService(undefined)); assert_throws(new TypeError(), () => BluetoothUUID.getService(undefined));
assert_throws('SyntaxError', () => BluetoothUUID.getService(null)); assert_throws(new TypeError(), () => BluetoothUUID.getService(null));
assert_throws('SyntaxError', () => BluetoothUUID.getService(false)); assert_throws(new TypeError(), () => BluetoothUUID.getService(false));
// getCharacteristic // getCharacteristic
assert_throws('SyntaxError', () => BluetoothUUID.getCharacteristic(object)); assert_throws(new TypeError(), () => BluetoothUUID.getCharacteristic(object));
assert_throws('SyntaxError', () => BluetoothUUID.getCharacteristic(array)); assert_throws(new TypeError(), () => BluetoothUUID.getCharacteristic(array));
assert_throws('SyntaxError', () => BluetoothUUID.getCharacteristic(func)); assert_throws(new TypeError(), () => BluetoothUUID.getCharacteristic(func));
assert_throws('SyntaxError', () => BluetoothUUID.getCharacteristic(undefined)); assert_throws(new TypeError(), () => BluetoothUUID.getCharacteristic(undefined));
assert_throws('SyntaxError', () => BluetoothUUID.getCharacteristic(null)); assert_throws(new TypeError(), () => BluetoothUUID.getCharacteristic(null));
assert_throws('SyntaxError', () => BluetoothUUID.getCharacteristic(false)); assert_throws(new TypeError(), () => BluetoothUUID.getCharacteristic(false));
// getDescriptor // getDescriptor
assert_throws('SyntaxError', () => BluetoothUUID.getDescriptor(object)); assert_throws(new TypeError(), () => BluetoothUUID.getDescriptor(object));
assert_throws('SyntaxError', () => BluetoothUUID.getDescriptor(array)); assert_throws(new TypeError(), () => BluetoothUUID.getDescriptor(array));
assert_throws('SyntaxError', () => BluetoothUUID.getDescriptor(func)); assert_throws(new TypeError(), () => BluetoothUUID.getDescriptor(func));
assert_throws('SyntaxError', () => BluetoothUUID.getDescriptor(undefined)); assert_throws(new TypeError(), () => BluetoothUUID.getDescriptor(undefined));
assert_throws('SyntaxError', () => BluetoothUUID.getDescriptor(null)); assert_throws(new TypeError(), () => BluetoothUUID.getDescriptor(null));
assert_throws('SyntaxError', () => BluetoothUUID.getDescriptor(false)); assert_throws(new TypeError(), () => BluetoothUUID.getDescriptor(false));
}, 'Non-number and non-strings'); }, 'Non-number and non-strings');
</script> </script>

View file

@ -81,8 +81,8 @@ promise_test(t => {
window.testRunner.setBluetoothMockDataSet(adapter_type.glucose_heart_rate); window.testRunner.setBluetoothMockDataSet(adapter_type.glucose_heart_rate);
let promises = []; let promises = [];
wrong_service_data_keys.forEach(args => { wrong_service_data_keys.forEach(args => {
promises.push(promise_rejects(t, 'SyntaxError', window.navigator.bluetooth.requestDevice(args))); promises.push(promise_rejects(t, new TypeError(), window.navigator.bluetooth.requestDevice(args)));
}); });
return Promise.all(promises); return Promise.all(promises);
}, 'Rejects with SyntaxError, if a serviceData key does not convert to a valid uuid.'); }, 'Rejects with TypeError, if a serviceData key does not convert to a valid uuid.');
</script> </script>

View file

@ -31,7 +31,7 @@ promise_test(t => {
window.testRunner.setBluetoothMockDataSet(adapter_type.empty); window.testRunner.setBluetoothMockDataSet(adapter_type.empty);
let promises = []; let promises = [];
test_specs.forEach(args => { test_specs.forEach(args => {
promises.push(promise_rejects(t, 'SyntaxError', window.navigator.bluetooth.requestDevice(args))); promises.push(promise_rejects(t, new TypeError(), window.navigator.bluetooth.requestDevice(args)));
}); });
return Promise.all(promises); return Promise.all(promises);
}, 'Invalid optional service must reject the promise.'); }, 'Invalid optional service must reject the promise.');

View file

@ -30,7 +30,7 @@ promise_test(t => {
window.testRunner.setBluetoothMockDataSet(adapter_type.empty); window.testRunner.setBluetoothMockDataSet(adapter_type.empty);
let promises = []; let promises = [];
test_specs.forEach(args => { test_specs.forEach(args => {
promises.push(promise_rejects(t, 'SyntaxError', window.navigator.bluetooth.requestDevice(args))); promises.push(promise_rejects(t, new TypeError(), window.navigator.bluetooth.requestDevice(args)));
}); });
return Promise.all(promises); return Promise.all(promises);
}, 'Invalid optional service must reject the promise.'); }, 'Invalid optional service must reject the promise.');