blob: ccd1e91ee1455cafbe73587c61c4ec95c3770f06 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::match_common::{get_composite_rules_from_composite_driver, node_to_device_property};
use crate::resolved_driver::ResolvedDriver;
use bind::compiler::symbol_table::{get_deprecated_key_identifier, get_deprecated_key_value};
use bind::compiler::Symbol;
use bind::interpreter::decode_bind_rules::DecodedRules;
use bind::interpreter::match_bind::{match_bind, DeviceProperties, MatchBindData, PropertyKey};
use fuchsia_zircon::{zx_status_t, Status};
use regex::Regex;
use std::collections::{BTreeMap, HashMap, HashSet};
use {fidl_fuchsia_driver_framework as fdf, fidl_fuchsia_driver_index as fdi};
const NAME_REGEX: &'static str = r"^[a-zA-Z0-9\-_]*$";
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct BindRuleCondition {
condition: fdf::Condition,
values: Vec<Symbol>,
}
type BindRules = BTreeMap<PropertyKey, BindRuleCondition>;
#[derive(Clone)]
pub struct CompositeParentRef {
// This is the spec name. Corresponds with the key of spec_list.
pub name: String,
// This is the parent index.
pub index: u32,
}
// The CompositeNodeSpecManager struct is responsible of managing a list of specs
// for matching.
pub struct CompositeNodeSpecManager {
// Maps a list of specs to the bind rules of their nodes. This is to handle multiple
// specs that share a node with the same bind rules. Used for matching nodes.
pub parent_refs: HashMap<BindRules, Vec<CompositeParentRef>>,
// Maps specs to the name. This list ensures that we don't add multiple specs with
// the same name.
pub spec_list: HashMap<String, fdf::CompositeInfo>,
}
impl CompositeNodeSpecManager {
pub fn new() -> Self {
CompositeNodeSpecManager { parent_refs: HashMap::new(), spec_list: HashMap::new() }
}
pub fn add_composite_node_spec(
&mut self,
spec: fdf::CompositeNodeSpec,
composite_drivers: Vec<&ResolvedDriver>,
) -> Result<(), i32> {
// Get and validate the name.
let name = spec.name.clone().ok_or(Status::INVALID_ARGS.into_raw())?;
if let Ok(name_regex) = Regex::new(NAME_REGEX) {
if !name_regex.is_match(&name) {
tracing::error!(
"Invalid spec name. Name can only contain [A-Za-z0-9-_] characters"
);
return Err(Status::INVALID_ARGS.into_raw());
}
} else {
tracing::warn!("Regex failure. Unable to validate spec name");
}
let parents = spec.parents.clone().ok_or(Status::INVALID_ARGS.into_raw())?;
if self.spec_list.contains_key(&name) {
return Err(Status::ALREADY_EXISTS.into_raw());
}
if parents.is_empty() {
return Err(Status::INVALID_ARGS.into_raw());
}
// Collect parent refs in a separate vector before adding them to the
// CompositeNodeSpecManager. This is to ensure that we add the parent refs after
// they're all verified to be valid.
// TODO(https://fxbug.dev/42056805): Update tests so that we can verify that properties exists in
// each parent ref.
let mut parent_refs: Vec<(BindRules, CompositeParentRef)> = vec![];
for (idx, parent) in parents.iter().enumerate() {
let properties = convert_fidl_to_bind_rules(&parent.bind_rules)?;
parent_refs
.push((properties, CompositeParentRef { name: name.clone(), index: idx as u32 }));
}
// Add each parent ref into the map.
for (properties, parent_ref) in parent_refs {
self.parent_refs
.entry(properties)
.and_modify(|refs| refs.push(parent_ref.clone()))
.or_insert(vec![parent_ref]);
}
let matched_composite_result =
self.find_composite_driver_match(&parents, &composite_drivers);
if let Some(matched_composite) = &matched_composite_result {
tracing::info!(
"Matched '{}' to composite node spec '{}'",
get_driver_url(matched_composite),
name
);
}
self.spec_list.insert(
name,
fdf::CompositeInfo {
spec: Some(spec),
matched_driver: matched_composite_result,
..Default::default()
},
);
Ok(())
}
// Match the given device properties to all the nodes. Returns a list of specs for all the
// nodes that match.
pub fn match_parent_specs(
&self,
properties: &DeviceProperties,
) -> Option<fdi::MatchDriverResult> {
let mut matching_refs: Vec<CompositeParentRef> = vec![];
for (node_props, parent_ref_list) in self.parent_refs.iter() {
if match_node(&node_props, properties) {
matching_refs.extend_from_slice(parent_ref_list.as_slice());
}
}
if matching_refs.is_empty() {
return None;
}
// Put in the matched composite info for this spec that we have stored in
// |spec_list|.
let mut composite_parents_result: Vec<fdf::CompositeParent> = vec![];
for matching_ref in matching_refs {
let composite_info = self.spec_list.get(&matching_ref.name);
match composite_info {
Some(info) => {
// TODO(https://fxbug.dev/42058749): Only return specs that have a matched composite using
// info.matched_driver.is_some()
composite_parents_result.push(fdf::CompositeParent {
composite: Some(fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&info.spec)),
matched_driver: info.matched_driver.clone(),
..Default::default()
}),
index: Some(matching_ref.index),
..Default::default()
});
}
None => {}
}
}
if composite_parents_result.is_empty() {
return None;
}
Some(fdi::MatchDriverResult::CompositeParents(composite_parents_result))
}
pub fn new_driver_available(&mut self, resolved_driver: &ResolvedDriver) {
// Only composite drivers should be matched against composite node specs.
if matches!(resolved_driver.bind_rules, DecodedRules::Normal(_)) {
return;
}
for (name, composite_info) in self.spec_list.iter_mut() {
if composite_info.matched_driver.is_some() {
continue;
}
let parents = composite_info.spec.as_ref().unwrap().parents.as_ref().unwrap();
let matched_composite_result = match_composite_properties(resolved_driver, parents);
if let Ok(Some(matched_composite)) = matched_composite_result {
tracing::info!(
"Matched '{}' to composite node spec '{}'",
get_driver_url(&matched_composite),
name
);
composite_info.matched_driver = Some(matched_composite);
}
}
}
pub fn rebind(
&mut self,
spec_name: String,
composite_drivers: Vec<&ResolvedDriver>,
) -> Result<(), zx_status_t> {
let composite_info = self.spec_list.get(&spec_name).ok_or(Status::NOT_FOUND.into_raw())?;
let parents = composite_info
.spec
.as_ref()
.ok_or(Status::INTERNAL.into_raw())?
.parents
.as_ref()
.ok_or(Status::INTERNAL.into_raw())?;
let new_match = self.find_composite_driver_match(parents, &composite_drivers);
self.spec_list.entry(spec_name).and_modify(|spec| {
spec.matched_driver = new_match;
});
Ok(())
}
pub fn rebind_composites_with_driver(
&mut self,
driver: String,
composite_drivers: Vec<&ResolvedDriver>,
) -> Result<(), zx_status_t> {
let specs_to_rebind = self
.spec_list
.iter()
.filter_map(|(spec_name, spec_info)| {
spec_info
.matched_driver
.as_ref()
.and_then(|matched_driver| matched_driver.composite_driver.as_ref())
.and_then(|composite_driver| composite_driver.driver_info.as_ref())
.and_then(|driver_info| driver_info.url.as_ref())
.and_then(|url| if &driver == url { Some(spec_name.to_string()) } else { None })
})
.collect::<Vec<_>>();
for spec_name in specs_to_rebind {
let composite_info =
self.spec_list.get(&spec_name).ok_or(Status::NOT_FOUND.into_raw())?;
let parents = composite_info
.spec
.as_ref()
.ok_or(Status::INTERNAL.into_raw())?
.parents
.as_ref()
.ok_or(Status::INTERNAL.into_raw())?;
let new_match = self.find_composite_driver_match(parents, &composite_drivers);
self.spec_list.entry(spec_name).and_modify(|spec| {
spec.matched_driver = new_match;
});
}
Ok(())
}
pub fn get_specs(&self, name_filter: Option<String>) -> Vec<fdf::CompositeInfo> {
if let Some(name) = name_filter {
match self.spec_list.get(&name) {
Some(item) => return vec![item.clone()],
None => return vec![],
}
};
let specs = self
.spec_list
.iter()
.map(|(_name, composite_info)| composite_info.clone())
.collect::<Vec<_>>();
return specs;
}
fn find_composite_driver_match<'a>(
&self,
parents: &'a Vec<fdf::ParentSpec>,
composite_drivers: &Vec<&ResolvedDriver>,
) -> Option<fdf::CompositeDriverMatch> {
for composite_driver in composite_drivers {
let matched_composite = match_composite_properties(composite_driver, parents);
if let Ok(Some(matched_composite)) = matched_composite {
return Some(matched_composite);
}
}
None
}
}
pub fn strip_parents_from_spec(spec: &Option<fdf::CompositeNodeSpec>) -> fdf::CompositeNodeSpec {
// Strip the parents of the rules and properties since they are not needed by
// the driver manager.
let parents_stripped = spec.as_ref().and_then(|spec| spec.parents.as_ref()).map(|parents| {
parents
.iter()
.map(|_parent| fdf::ParentSpec { bind_rules: vec![], properties: vec![] })
.collect::<Vec<_>>()
});
fdf::CompositeNodeSpec {
name: spec.as_ref().and_then(|spec| spec.name.clone()),
parents: parents_stripped,
..Default::default()
}
}
fn convert_fidl_to_bind_rules(
fidl_bind_rules: &Vec<fdf::BindRule>,
) -> Result<BindRules, zx_status_t> {
if fidl_bind_rules.is_empty() {
return Err(Status::INVALID_ARGS.into_raw());
}
let mut bind_rules = BTreeMap::new();
for fidl_rule in fidl_bind_rules {
let key = match &fidl_rule.key {
fdf::NodePropertyKey::IntValue(i) => PropertyKey::NumberKey(i.clone().into()),
fdf::NodePropertyKey::StringValue(s) => PropertyKey::StringKey(s.clone()),
};
// Check if the properties contain duplicate keys.
if bind_rules.contains_key(&key) {
return Err(Status::INVALID_ARGS.into_raw());
}
let first_val = fidl_rule.values.first().ok_or(Status::INVALID_ARGS.into_raw())?;
let values = fidl_rule
.values
.iter()
.map(|val| {
// Check that the properties are all the same type.
if std::mem::discriminant(first_val) != std::mem::discriminant(val) {
return Err(Status::INVALID_ARGS.into_raw());
}
Ok(node_property_to_symbol(val)?)
})
.collect::<Result<Vec<Symbol>, zx_status_t>>()?;
bind_rules
.insert(key, BindRuleCondition { condition: fidl_rule.condition, values: values });
}
Ok(bind_rules)
}
fn match_node(bind_rules: &BindRules, device_properties: &DeviceProperties) -> bool {
for (key, node_prop_values) in bind_rules.iter() {
let mut dev_prop_contains_value = match device_properties.get(key) {
Some(val) => node_prop_values.values.contains(val),
None => false,
};
// If the properties don't contain the key, try to convert it to a deprecated
// key and check the properties with it.
if !dev_prop_contains_value && !device_properties.contains_key(key) {
let deprecated_key = match key {
PropertyKey::NumberKey(int_key) => get_deprecated_key_identifier(*int_key as u32)
.map(|key| PropertyKey::StringKey(key)),
PropertyKey::StringKey(str_key) => {
get_deprecated_key_value(str_key).map(|key| PropertyKey::NumberKey(key as u64))
}
};
if let Some(key) = deprecated_key {
dev_prop_contains_value = match device_properties.get(&key) {
Some(val) => node_prop_values.values.contains(val),
None => false,
};
}
}
let evaluate_condition = match node_prop_values.condition {
fdf::Condition::Accept => {
// If the node property accepts a false boolean value and the property is
// missing from the device properties, then we should evaluate the condition
// as true.
dev_prop_contains_value
|| node_prop_values.values.contains(&Symbol::BoolValue(false))
}
fdf::Condition::Reject => !dev_prop_contains_value,
fdf::Condition::Unknown => {
tracing::error!("Invalid condition type in bind rules.");
return false;
}
};
if !evaluate_condition {
return false;
}
}
true
}
fn node_property_to_symbol(value: &fdf::NodePropertyValue) -> Result<Symbol, zx_status_t> {
match value {
fdf::NodePropertyValue::IntValue(i) => {
Ok(bind::compiler::Symbol::NumberValue(i.clone().into()))
}
fdf::NodePropertyValue::StringValue(s) => {
Ok(bind::compiler::Symbol::StringValue(s.clone()))
}
fdf::NodePropertyValue::EnumValue(s) => Ok(bind::compiler::Symbol::EnumValue(s.clone())),
fdf::NodePropertyValue::BoolValue(b) => Ok(bind::compiler::Symbol::BoolValue(b.clone())),
_ => Err(Status::INVALID_ARGS.into_raw()),
}
}
fn match_composite_properties<'a>(
composite_driver: &'a ResolvedDriver,
parents: &'a Vec<fdf::ParentSpec>,
) -> Result<Option<fdf::CompositeDriverMatch>, i32> {
// The spec must have at least 1 node to match a composite driver.
if parents.len() < 1 {
return Ok(None);
}
let composite = get_composite_rules_from_composite_driver(composite_driver)?;
// The composite driver bind rules should have a total node count of more than or equal to the
// total node count of the spec. This is to account for optional nodes in the
// composite driver bind rules.
if composite.optional_nodes.len() + composite.additional_nodes.len() + 1 < parents.len() {
return Ok(None);
}
// First find a matching primary node.
let mut primary_parent_index = 0;
let mut primary_matches = false;
for i in 0..parents.len() {
primary_matches = node_matches_composite_driver(
&parents[i],
&composite.primary_node.instructions,
&composite.symbol_table,
);
if primary_matches {
primary_parent_index = i as u32;
break;
}
}
if !primary_matches {
return Ok(None);
}
// The remaining nodes in the properties can match the
// additional nodes in the bind rules in any order.
//
// This logic has one issue that we are accepting as a tradeoff for simplicity:
// If a properties node can match to multiple bind rule
// additional nodes, it is going to take the first one, even if there is a less strict
// node that it can take. This can lead to false negative matches.
//
// Example:
// properties[1] can match both additional_nodes[0] and additional_nodes[1]
// properties[2] can only match additional_nodes[0]
//
// This algorithm will return false because it matches up properties[1] with
// additional_nodes[0], and so properties[2] can't match the remaining nodes
// [additional_nodes[1]].
//
// If we were smarter here we could match up properties[1] with additional_nodes[1]
// and properties[2] with additional_nodes[0] to return a positive match.
// TODO(https://fxbug.dev/42058532): Disallow ambiguity with spec matching. We should log
// a warning and return false if a spec node matches with multiple composite
// driver nodes, and vice versa.
let mut unmatched_additional_indices =
(0..composite.additional_nodes.len()).collect::<HashSet<_>>();
let mut unmatched_optional_indices =
(0..composite.optional_nodes.len()).collect::<HashSet<_>>();
let mut parent_names = vec![];
for i in 0..parents.len() {
if i == primary_parent_index as usize {
parent_names.push(composite.symbol_table[&composite.primary_node.name_id].clone());
continue;
}
let mut matched = None;
let mut matched_name: Option<String> = None;
let mut from_optional = false;
// First check if any of the additional nodes match it.
for &j in &unmatched_additional_indices {
let matches = node_matches_composite_driver(
&parents[i],
&composite.additional_nodes[j].instructions,
&composite.symbol_table,
);
if matches {
matched = Some(j);
matched_name =
Some(composite.symbol_table[&composite.additional_nodes[j].name_id].clone());
break;
}
}
// If no additional nodes matched it, then look in the optional nodes.
if matched.is_none() {
for &j in &unmatched_optional_indices {
let matches = node_matches_composite_driver(
&parents[i],
&composite.optional_nodes[j].instructions,
&composite.symbol_table,
);
if matches {
from_optional = true;
matched = Some(j);
matched_name =
Some(composite.symbol_table[&composite.optional_nodes[j].name_id].clone());
break;
}
}
}
if matched.is_none() {
return Ok(None);
}
if from_optional {
unmatched_optional_indices.remove(&matched.unwrap());
} else {
unmatched_additional_indices.remove(&matched.unwrap());
}
parent_names.push(matched_name.unwrap());
}
// If we didn't consume all of the additional nodes in the bind rules then this is not a match.
if !unmatched_additional_indices.is_empty() {
return Ok(None);
}
let driver = fdf::CompositeDriverInfo {
composite_name: Some(composite.symbol_table[&composite.device_name_id].clone()),
driver_info: Some(composite_driver.create_driver_info(false)),
..Default::default()
};
return Ok(Some(fdf::CompositeDriverMatch {
composite_driver: Some(driver),
parent_names: Some(parent_names),
primary_parent_index: Some(primary_parent_index),
..Default::default()
}));
}
fn node_matches_composite_driver(
node: &fdf::ParentSpec,
bind_rules_node: &Vec<u8>,
symbol_table: &HashMap<u32, String>,
) -> bool {
match node_to_device_property(&node.properties) {
Err(_) => false,
Ok(props) => {
let match_bind_data = MatchBindData { symbol_table, instructions: bind_rules_node };
match_bind(match_bind_data, &props).unwrap_or(false)
}
}
}
fn get_driver_url(composite: &fdf::CompositeDriverMatch) -> String {
return composite
.composite_driver
.as_ref()
.and_then(|driver| driver.driver_info.as_ref())
.and_then(|driver_info| driver_info.url.clone())
.unwrap_or("".to_string());
}
#[cfg(test)]
mod tests {
use super::*;
use crate::resolved_driver::DriverPackageType;
use bind::compiler::{
CompiledBindRules, CompositeBindRules, CompositeNode, SymbolicInstruction,
SymbolicInstructionInfo,
};
use bind::parser::bind_library::ValueType;
use fuchsia_async as fasync;
const TEST_DEVICE_NAME: &str = "test_device";
const TEST_PRIMARY_NAME: &str = "primary_node";
const TEST_ADDITIONAL_A_NAME: &str = "node_a";
const TEST_ADDITIONAL_B_NAME: &str = "node_b";
const TEST_OPTIONAL_NAME: &str = "optional_node";
fn make_accept(key: fdf::NodePropertyKey, value: fdf::NodePropertyValue) -> fdf::BindRule {
fdf::BindRule { key: key, condition: fdf::Condition::Accept, values: vec![value] }
}
fn make_accept_list(
key: fdf::NodePropertyKey,
values: Vec<fdf::NodePropertyValue>,
) -> fdf::BindRule {
fdf::BindRule { key: key, condition: fdf::Condition::Accept, values: values }
}
fn make_reject(key: fdf::NodePropertyKey, value: fdf::NodePropertyValue) -> fdf::BindRule {
fdf::BindRule { key: key, condition: fdf::Condition::Reject, values: vec![value] }
}
fn make_reject_list(
key: fdf::NodePropertyKey,
values: Vec<fdf::NodePropertyValue>,
) -> fdf::BindRule {
fdf::BindRule { key: key, condition: fdf::Condition::Reject, values: values }
}
fn make_property(
key: fdf::NodePropertyKey,
value: fdf::NodePropertyValue,
) -> fdf::NodeProperty {
fdf::NodeProperty { key: key, value: value }
}
// TODO(https://fxbug.dev/42071377): Update tests so that they use the test data functions more often.
fn create_test_parent_spec_1() -> fdf::ParentSpec {
let bind_rules = vec![
make_accept(fdf::NodePropertyKey::IntValue(1), fdf::NodePropertyValue::IntValue(200)),
make_accept(fdf::NodePropertyKey::IntValue(3), fdf::NodePropertyValue::BoolValue(true)),
make_accept(
fdf::NodePropertyKey::StringValue("killdeer".to_string()),
fdf::NodePropertyValue::StringValue("plover".to_string()),
),
];
let properties = vec![make_property(
fdf::NodePropertyKey::IntValue(2),
fdf::NodePropertyValue::BoolValue(false),
)];
fdf::ParentSpec { bind_rules: bind_rules, properties: properties }
}
fn create_test_parent_spec_2() -> fdf::ParentSpec {
let bind_rules = vec![
make_reject(
fdf::NodePropertyKey::StringValue("killdeer".to_string()),
fdf::NodePropertyValue::StringValue("plover".to_string()),
),
make_accept(
fdf::NodePropertyKey::StringValue("flycatcher".to_string()),
fdf::NodePropertyValue::EnumValue("flycatcher.phoebe".to_string()),
),
make_reject(
fdf::NodePropertyKey::StringValue("yellowlegs".to_string()),
fdf::NodePropertyValue::BoolValue(true),
),
];
let properties = vec![make_property(
fdf::NodePropertyKey::IntValue(3),
fdf::NodePropertyValue::BoolValue(true),
)];
fdf::ParentSpec { bind_rules: bind_rules, properties: properties }
}
fn create_driver<'a>(
composite_name: String,
primary_node: (&str, Vec<SymbolicInstructionInfo<'a>>),
additionals: Vec<(&str, Vec<SymbolicInstructionInfo<'a>>)>,
optionals: Vec<(&str, Vec<SymbolicInstructionInfo<'a>>)>,
) -> ResolvedDriver {
let mut additional_nodes = vec![];
let mut optional_nodes = vec![];
for additional in additionals {
additional_nodes
.push(CompositeNode { name: additional.0.to_string(), instructions: additional.1 });
}
for optional in optionals {
optional_nodes
.push(CompositeNode { name: optional.0.to_string(), instructions: optional.1 });
}
let bind_rules = CompositeBindRules {
device_name: composite_name,
symbol_table: HashMap::new(),
primary_node: CompositeNode {
name: primary_node.0.to_string(),
instructions: primary_node.1,
},
additional_nodes: additional_nodes,
optional_nodes: optional_nodes,
enable_debug: false,
};
let bytecode = CompiledBindRules::CompositeBind(bind_rules).encode_to_bytecode().unwrap();
let rules = DecodedRules::new(bytecode).unwrap();
ResolvedDriver {
component_url: url::Url::parse("fuchsia-pkg://fuchsia.com/package#driver/my-driver.cm")
.unwrap(),
bind_rules: rules,
bind_bytecode: vec![],
colocate: false,
device_categories: vec![],
fallback: false,
package_type: DriverPackageType::Base,
package_hash: None,
is_dfv2: None,
disabled: false,
}
}
fn create_driver_with_rules<'a>(
primary_node: (&str, Vec<SymbolicInstructionInfo<'a>>),
additionals: Vec<(&str, Vec<SymbolicInstructionInfo<'a>>)>,
optionals: Vec<(&str, Vec<SymbolicInstructionInfo<'a>>)>,
) -> ResolvedDriver {
create_driver(TEST_DEVICE_NAME.to_string(), primary_node, additionals, optionals)
}
#[fasync::run_singlethreaded(test)]
async fn test_property_match_node() {
let nodes = Some(vec![create_test_parent_spec_1(), create_test_parent_spec_2()]);
let composite_spec = fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: nodes.clone(),
..Default::default()
};
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(composite_spec.clone(), vec![])
);
assert_eq!(1, composite_node_spec_manager.get_specs(None).len());
assert_eq!(0, composite_node_spec_manager.get_specs(Some("not_there".to_string())).len());
let specs = composite_node_spec_manager.get_specs(Some("test_spec".to_string()));
assert_eq!(1, specs.len());
let composite_node_spec = &specs[0];
let expected_composite_node_spec =
fdf::CompositeInfo { spec: Some(composite_spec.clone()), ..Default::default() };
assert_eq!(&expected_composite_node_spec, composite_node_spec);
// Match node 1.
let mut device_properties_1: DeviceProperties = HashMap::new();
device_properties_1.insert(PropertyKey::NumberKey(1), Symbol::NumberValue(200));
device_properties_1.insert(
PropertyKey::StringKey("kingfisher".to_string()),
Symbol::StringValue("kookaburra".to_string()),
);
device_properties_1.insert(PropertyKey::NumberKey(3), Symbol::BoolValue(true));
device_properties_1.insert(
PropertyKey::StringKey("killdeer".to_string()),
Symbol::StringValue("plover".to_string()),
);
let expected_parent = fdf::CompositeParent {
composite: Some(fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec.clone()))),
..Default::default()
}),
index: Some(0),
..Default::default()
};
assert_eq!(
Some(fdi::MatchDriverResult::CompositeParents(vec![expected_parent])),
composite_node_spec_manager.match_parent_specs(&device_properties_1)
);
// Match node 2.
let mut device_properties_2: DeviceProperties = HashMap::new();
device_properties_2
.insert(PropertyKey::StringKey("yellowlegs".to_string()), Symbol::BoolValue(false));
device_properties_2.insert(
PropertyKey::StringKey("killdeer".to_string()),
Symbol::StringValue("lapwing".to_string()),
);
device_properties_2.insert(
PropertyKey::StringKey("flycatcher".to_string()),
Symbol::EnumValue("flycatcher.phoebe".to_string()),
);
let expected_parent_2 = fdf::CompositeParent {
composite: Some(fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec.clone()))),
..Default::default()
}),
index: Some(1),
..Default::default()
};
assert_eq!(
Some(fdi::MatchDriverResult::CompositeParents(vec![expected_parent_2])),
composite_node_spec_manager.match_parent_specs(&device_properties_2)
);
}
#[fasync::run_singlethreaded(test)]
async fn test_property_match_bool_edgecase() {
let bind_rules = vec![
make_accept(fdf::NodePropertyKey::IntValue(1), fdf::NodePropertyValue::IntValue(200)),
make_accept(
fdf::NodePropertyKey::IntValue(3),
fdf::NodePropertyValue::BoolValue(false),
),
];
let properties = vec![make_property(
fdf::NodePropertyKey::IntValue(3),
fdf::NodePropertyValue::BoolValue(true),
)];
let composite_spec = fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: Some(vec![fdf::ParentSpec { bind_rules: bind_rules, properties: properties }]),
..Default::default()
};
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(composite_spec.clone(), vec![])
);
// Match node.
let mut device_properties: DeviceProperties = HashMap::new();
device_properties.insert(PropertyKey::NumberKey(1), Symbol::NumberValue(200));
let expected_parent = fdf::CompositeParent {
composite: Some(fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec.clone()))),
..Default::default()
}),
index: Some(0),
..Default::default()
};
assert_eq!(
Some(fdi::MatchDriverResult::CompositeParents(vec![expected_parent])),
composite_node_spec_manager.match_parent_specs(&device_properties)
);
}
#[fasync::run_singlethreaded(test)]
async fn test_deprecated_keys_match() {
let bind_rules = vec![
make_accept(
fdf::NodePropertyKey::StringValue("fuchsia.BIND_PROTOCOL".to_string()),
fdf::NodePropertyValue::IntValue(200),
),
make_accept(
fdf::NodePropertyKey::IntValue(0x0201), // "fuchsia.BIND_USB_PID"
fdf::NodePropertyValue::IntValue(10),
),
];
let properties = vec![make_property(
fdf::NodePropertyKey::IntValue(0x01),
fdf::NodePropertyValue::IntValue(50),
)];
let composite_spec = fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: Some(vec![fdf::ParentSpec { bind_rules: bind_rules, properties: properties }]),
..Default::default()
};
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(composite_spec.clone(), vec![])
);
// Match node.
let mut device_properties: DeviceProperties = HashMap::new();
device_properties.insert(
PropertyKey::NumberKey(1), /* "fuchsia.BIND_PROTOCOL" */
Symbol::NumberValue(200),
);
device_properties.insert(
PropertyKey::StringKey("fuchsia.BIND_USB_PID".to_string()),
Symbol::NumberValue(10),
);
let expected_parent = fdf::CompositeParent {
composite: Some(fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec.clone()))),
..Default::default()
}),
index: Some(0),
..Default::default()
};
assert_eq!(
Some(fdi::MatchDriverResult::CompositeParents(vec![expected_parent])),
composite_node_spec_manager.match_parent_specs(&device_properties)
);
}
#[fasync::run_singlethreaded(test)]
async fn test_multiple_spec_match() {
let bind_rules_2_rearranged = vec![
make_accept(
fdf::NodePropertyKey::StringValue("flycatcher".to_string()),
fdf::NodePropertyValue::EnumValue("flycatcher.phoebe".to_string()),
),
make_reject(
fdf::NodePropertyKey::StringValue("killdeer".to_string()),
fdf::NodePropertyValue::StringValue("plover".to_string()),
),
make_reject(
fdf::NodePropertyKey::StringValue("yellowlegs".to_string()),
fdf::NodePropertyValue::BoolValue(true),
),
];
let properties_2 = vec![make_property(
fdf::NodePropertyKey::IntValue(3),
fdf::NodePropertyValue::BoolValue(true),
)];
let bind_rules_3 = vec![make_accept(
fdf::NodePropertyKey::StringValue("cormorant".to_string()),
fdf::NodePropertyValue::BoolValue(true),
)];
let properties_3 = vec![make_property(
fdf::NodePropertyKey::StringValue("anhinga".to_string()),
fdf::NodePropertyValue::BoolValue(false),
)];
let composite_spec_1 = fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: Some(vec![create_test_parent_spec_1(), create_test_parent_spec_2()]),
..Default::default()
};
let composite_spec_2 = fdf::CompositeNodeSpec {
name: Some("test_spec2".to_string()),
parents: Some(vec![
fdf::ParentSpec { bind_rules: bind_rules_2_rearranged, properties: properties_2 },
fdf::ParentSpec { bind_rules: bind_rules_3, properties: properties_3 },
]),
..Default::default()
};
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(composite_spec_1.clone(), vec![])
);
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(composite_spec_2.clone(), vec![])
);
// Match node.
let mut device_properties: DeviceProperties = HashMap::new();
device_properties
.insert(PropertyKey::StringKey("yellowlegs".to_string()), Symbol::BoolValue(false));
device_properties.insert(
PropertyKey::StringKey("killdeer".to_string()),
Symbol::StringValue("lapwing".to_string()),
);
device_properties.insert(
PropertyKey::StringKey("flycatcher".to_string()),
Symbol::EnumValue("flycatcher.phoebe".to_string()),
);
let match_result =
composite_node_spec_manager.match_parent_specs(&device_properties).unwrap();
assert!(
if let fdi::MatchDriverResult::CompositeParents(matched_node_info) = match_result {
assert_eq!(2, matched_node_info.len());
assert!(matched_node_info.contains(&fdf::CompositeParent {
composite: Some(fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec_1.clone()))),
..Default::default()
}),
index: Some(1),
..Default::default()
}));
assert!(matched_node_info.contains(&fdf::CompositeParent {
composite: Some(fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec_2.clone()))),
..Default::default()
}),
index: Some(0),
..Default::default()
}));
true
} else {
false
}
);
}
#[fasync::run_singlethreaded(test)]
async fn test_multiple_spec_nodes_match() {
let bind_rules_1 = vec![
make_accept(fdf::NodePropertyKey::IntValue(1), fdf::NodePropertyValue::IntValue(200)),
make_accept(
fdf::NodePropertyKey::StringValue("killdeer".to_string()),
fdf::NodePropertyValue::StringValue("plover".to_string()),
),
];
let properties_1 = vec![make_property(
fdf::NodePropertyKey::IntValue(2),
fdf::NodePropertyValue::BoolValue(false),
)];
let bind_rules_1_rearranged = vec![
make_accept(
fdf::NodePropertyKey::StringValue("killdeer".to_string()),
fdf::NodePropertyValue::StringValue("plover".to_string()),
),
make_accept(fdf::NodePropertyKey::IntValue(1), fdf::NodePropertyValue::IntValue(200)),
];
let bind_rules_3 = vec![make_accept(
fdf::NodePropertyKey::StringValue("cormorant".to_string()),
fdf::NodePropertyValue::BoolValue(true),
)];
let properties_3 = vec![make_property(
fdf::NodePropertyKey::IntValue(3),
fdf::NodePropertyValue::BoolValue(false),
)];
let bind_rules_4 = vec![make_accept_list(
fdf::NodePropertyKey::IntValue(1),
vec![fdf::NodePropertyValue::IntValue(10), fdf::NodePropertyValue::IntValue(200)],
)];
let properties_4 = vec![make_property(
fdf::NodePropertyKey::IntValue(2),
fdf::NodePropertyValue::BoolValue(true),
)];
let composite_spec_1 = fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: Some(vec![
fdf::ParentSpec { bind_rules: bind_rules_1, properties: properties_1.clone() },
create_test_parent_spec_2(),
]),
..Default::default()
};
let composite_spec_2 = fdf::CompositeNodeSpec {
name: Some("test_spec2".to_string()),
parents: Some(vec![
fdf::ParentSpec { bind_rules: bind_rules_3, properties: properties_3 },
fdf::ParentSpec { bind_rules: bind_rules_1_rearranged, properties: properties_1 },
]),
..Default::default()
};
let composite_spec_3 = fdf::CompositeNodeSpec {
name: Some("test_spec3".to_string()),
parents: Some(vec![fdf::ParentSpec {
bind_rules: bind_rules_4,
properties: properties_4,
}]),
..Default::default()
};
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(composite_spec_1.clone(), vec![])
);
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(composite_spec_2.clone(), vec![])
);
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(composite_spec_3.clone(), vec![])
);
// Match node.
let mut device_properties: DeviceProperties = HashMap::new();
device_properties.insert(PropertyKey::NumberKey(1), Symbol::NumberValue(200));
device_properties.insert(
PropertyKey::StringKey("killdeer".to_string()),
Symbol::StringValue("plover".to_string()),
);
let match_result =
composite_node_spec_manager.match_parent_specs(&device_properties).unwrap();
assert!(
if let fdi::MatchDriverResult::CompositeParents(matched_node_info) = match_result {
assert_eq!(3, matched_node_info.len());
assert!(matched_node_info.contains(&fdf::CompositeParent {
composite: Some(fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec_1.clone()))),
..Default::default()
}),
index: Some(0),
..Default::default()
}));
assert!(matched_node_info.contains(&fdf::CompositeParent {
composite: Some(fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec_2.clone()))),
..Default::default()
}),
index: Some(1),
..Default::default()
}));
assert!(matched_node_info.contains(&fdf::CompositeParent {
composite: Some(fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec_3.clone()))),
..Default::default()
}),
index: Some(0),
..Default::default()
}));
true
} else {
false
}
);
}
#[fasync::run_singlethreaded(test)]
async fn test_property_mismatch() {
let bind_rules_2 = vec![
make_accept(
fdf::NodePropertyKey::StringValue("killdeer".to_string()),
fdf::NodePropertyValue::StringValue("plover".to_string()),
),
make_reject(
fdf::NodePropertyKey::StringValue("yellowlegs".to_string()),
fdf::NodePropertyValue::BoolValue(false),
),
];
let properties_2 = vec![make_property(
fdf::NodePropertyKey::IntValue(3),
fdf::NodePropertyValue::BoolValue(true),
)];
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(
fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: Some(vec![
create_test_parent_spec_1(),
fdf::ParentSpec { bind_rules: bind_rules_2, properties: properties_2 },
]),
..Default::default()
},
vec![]
)
);
let mut device_properties: DeviceProperties = HashMap::new();
device_properties.insert(PropertyKey::NumberKey(1), Symbol::NumberValue(200));
device_properties.insert(
PropertyKey::StringKey("kingfisher".to_string()),
Symbol::StringValue("bee-eater".to_string()),
);
device_properties
.insert(PropertyKey::StringKey("yellowlegs".to_string()), Symbol::BoolValue(false));
device_properties.insert(
PropertyKey::StringKey("killdeer".to_string()),
Symbol::StringValue("plover".to_string()),
);
assert_eq!(None, composite_node_spec_manager.match_parent_specs(&device_properties));
}
#[fasync::run_singlethreaded(test)]
async fn test_property_match_list() {
let bind_rules_1 = vec![
make_reject_list(
fdf::NodePropertyKey::IntValue(10),
vec![fdf::NodePropertyValue::IntValue(200), fdf::NodePropertyValue::IntValue(150)],
),
make_accept_list(
fdf::NodePropertyKey::StringValue("plover".to_string()),
vec![
fdf::NodePropertyValue::StringValue("killdeer".to_string()),
fdf::NodePropertyValue::StringValue("lapwing".to_string()),
],
),
];
let properties_1 = vec![make_property(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(100),
)];
let bind_rules_2 = vec![
make_reject_list(
fdf::NodePropertyKey::IntValue(11),
vec![fdf::NodePropertyValue::IntValue(20), fdf::NodePropertyValue::IntValue(10)],
),
make_accept(
fdf::NodePropertyKey::StringValue("dunlin".to_string()),
fdf::NodePropertyValue::BoolValue(true),
),
];
let properties_2 = vec![make_property(
fdf::NodePropertyKey::IntValue(3),
fdf::NodePropertyValue::BoolValue(true),
)];
let composite_spec = fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: Some(vec![
fdf::ParentSpec { bind_rules: bind_rules_1, properties: properties_1 },
fdf::ParentSpec { bind_rules: bind_rules_2, properties: properties_2 },
]),
..Default::default()
};
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(composite_spec.clone(), vec![])
);
// Match node 1.
let mut device_properties_1: DeviceProperties = HashMap::new();
device_properties_1.insert(PropertyKey::NumberKey(10), Symbol::NumberValue(20));
device_properties_1.insert(
PropertyKey::StringKey("plover".to_string()),
Symbol::StringValue("lapwing".to_string()),
);
let expected_parent_1 = fdf::CompositeParent {
composite: Some(fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec.clone()))),
..Default::default()
}),
index: Some(0),
..Default::default()
};
assert_eq!(
Some(fdi::MatchDriverResult::CompositeParents(vec![expected_parent_1])),
composite_node_spec_manager.match_parent_specs(&device_properties_1)
);
// Match node 2.
let mut device_properties_2: DeviceProperties = HashMap::new();
device_properties_2.insert(PropertyKey::NumberKey(5), Symbol::NumberValue(20));
device_properties_2
.insert(PropertyKey::StringKey("dunlin".to_string()), Symbol::BoolValue(true));
let expected_parent_2 = fdf::CompositeParent {
composite: Some(fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec.clone()))),
..Default::default()
}),
index: Some(1),
..Default::default()
};
assert_eq!(
Some(fdi::MatchDriverResult::CompositeParents(vec![expected_parent_2])),
composite_node_spec_manager.match_parent_specs(&device_properties_2)
);
}
#[fasync::run_singlethreaded(test)]
async fn test_property_mismatch_list() {
let bind_rules_1 = vec![
make_reject_list(
fdf::NodePropertyKey::IntValue(10),
vec![fdf::NodePropertyValue::IntValue(200), fdf::NodePropertyValue::IntValue(150)],
),
make_accept_list(
fdf::NodePropertyKey::StringValue("plover".to_string()),
vec![
fdf::NodePropertyValue::StringValue("killdeer".to_string()),
fdf::NodePropertyValue::StringValue("lapwing".to_string()),
],
),
];
let properties_1 = vec![make_property(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(100),
)];
let bind_rules_2 = vec![
make_reject_list(
fdf::NodePropertyKey::IntValue(11),
vec![fdf::NodePropertyValue::IntValue(20), fdf::NodePropertyValue::IntValue(10)],
),
make_accept(fdf::NodePropertyKey::IntValue(2), fdf::NodePropertyValue::BoolValue(true)),
];
let properties_2 = vec![make_property(
fdf::NodePropertyKey::IntValue(3),
fdf::NodePropertyValue::BoolValue(true),
)];
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(
fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: Some(vec![
fdf::ParentSpec { bind_rules: bind_rules_1, properties: properties_1 },
fdf::ParentSpec { bind_rules: bind_rules_2, properties: properties_2 },
]),
..Default::default()
},
vec![]
)
);
// Match node 1.
let mut device_properties_1: DeviceProperties = HashMap::new();
device_properties_1.insert(PropertyKey::NumberKey(10), Symbol::NumberValue(200));
device_properties_1.insert(
PropertyKey::StringKey("plover".to_string()),
Symbol::StringValue("lapwing".to_string()),
);
assert_eq!(None, composite_node_spec_manager.match_parent_specs(&device_properties_1));
// Match node 2.
let mut device_properties_2: DeviceProperties = HashMap::new();
device_properties_2.insert(PropertyKey::NumberKey(11), Symbol::NumberValue(10));
device_properties_2.insert(PropertyKey::NumberKey(2), Symbol::BoolValue(true));
assert_eq!(None, composite_node_spec_manager.match_parent_specs(&device_properties_2));
}
#[fasync::run_singlethreaded(test)]
async fn test_property_multiple_value_types() {
let bind_rules = vec![make_reject_list(
fdf::NodePropertyKey::IntValue(10),
vec![fdf::NodePropertyValue::IntValue(200), fdf::NodePropertyValue::BoolValue(false)],
)];
let properties = vec![make_property(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(100),
)];
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Err(Status::INVALID_ARGS.into_raw()),
composite_node_spec_manager.add_composite_node_spec(
fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: Some(vec![fdf::ParentSpec {
bind_rules: bind_rules,
properties: properties,
}]),
..Default::default()
},
vec![]
)
);
assert!(composite_node_spec_manager.parent_refs.is_empty());
assert!(composite_node_spec_manager.spec_list.is_empty());
}
#[fasync::run_singlethreaded(test)]
async fn test_property_duplicate_key() {
let bind_rules = vec![
make_reject_list(
fdf::NodePropertyKey::IntValue(10),
vec![fdf::NodePropertyValue::IntValue(200), fdf::NodePropertyValue::IntValue(150)],
),
make_accept(fdf::NodePropertyKey::IntValue(10), fdf::NodePropertyValue::IntValue(10)),
];
let properties = vec![make_property(
fdf::NodePropertyKey::IntValue(3),
fdf::NodePropertyValue::BoolValue(true),
)];
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Err(Status::INVALID_ARGS.into_raw()),
composite_node_spec_manager.add_composite_node_spec(
fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: Some(vec![fdf::ParentSpec {
bind_rules: bind_rules,
properties: properties,
},]),
..Default::default()
},
vec![]
)
);
assert!(composite_node_spec_manager.parent_refs.is_empty());
assert!(composite_node_spec_manager.spec_list.is_empty());
}
#[fasync::run_singlethreaded(test)]
async fn test_missing_bind_rules() {
let bind_rules = vec![
make_reject_list(
fdf::NodePropertyKey::IntValue(10),
vec![fdf::NodePropertyValue::IntValue(200), fdf::NodePropertyValue::IntValue(150)],
),
make_accept(fdf::NodePropertyKey::IntValue(10), fdf::NodePropertyValue::IntValue(10)),
];
let properties_1 = vec![make_property(
fdf::NodePropertyKey::IntValue(3),
fdf::NodePropertyValue::BoolValue(true),
)];
let properties_2 = vec![make_property(
fdf::NodePropertyKey::IntValue(10),
fdf::NodePropertyValue::BoolValue(false),
)];
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Err(Status::INVALID_ARGS.into_raw()),
composite_node_spec_manager.add_composite_node_spec(
fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: Some(vec![
fdf::ParentSpec { bind_rules: bind_rules, properties: properties_1 },
fdf::ParentSpec { bind_rules: vec![], properties: properties_2 },
]),
..Default::default()
},
vec![]
)
);
assert!(composite_node_spec_manager.parent_refs.is_empty());
assert!(composite_node_spec_manager.spec_list.is_empty());
}
#[fasync::run_singlethreaded(test)]
async fn test_missing_composite_node_spec_fields() {
let bind_rules = vec![
make_reject_list(
fdf::NodePropertyKey::IntValue(10),
vec![fdf::NodePropertyValue::IntValue(200), fdf::NodePropertyValue::IntValue(150)],
),
make_accept(fdf::NodePropertyKey::IntValue(10), fdf::NodePropertyValue::IntValue(10)),
];
let properties_1 = vec![make_property(
fdf::NodePropertyKey::IntValue(3),
fdf::NodePropertyValue::BoolValue(true),
)];
let properties_2 = vec![make_property(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::BoolValue(false),
)];
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Err(Status::INVALID_ARGS.into_raw()),
composite_node_spec_manager.add_composite_node_spec(
fdf::CompositeNodeSpec {
name: None,
parents: Some(vec![
fdf::ParentSpec { bind_rules: bind_rules, properties: properties_1 },
fdf::ParentSpec { bind_rules: vec![], properties: properties_2 },
]),
..Default::default()
},
vec![]
)
);
assert!(composite_node_spec_manager.parent_refs.is_empty());
assert!(composite_node_spec_manager.spec_list.is_empty());
assert_eq!(
Err(Status::INVALID_ARGS.into_raw()),
composite_node_spec_manager.add_composite_node_spec(
fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: None,
..Default::default()
},
vec![]
)
);
assert!(composite_node_spec_manager.parent_refs.is_empty());
assert!(composite_node_spec_manager.spec_list.is_empty());
}
#[fasync::run_singlethreaded(test)]
async fn test_composite_match() {
let primary_bind_rules = vec![make_accept(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(200),
)];
let additional_bind_rules_1 = vec![make_accept(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(10),
)];
let additional_bind_rules_2 = vec![make_accept(
fdf::NodePropertyKey::IntValue(10),
fdf::NodePropertyValue::BoolValue(true),
)];
let primary_key_1 = "whimbrel";
let primary_val_1 = "sanderling";
let additional_a_key_1 = 100;
let additional_a_val_1 = 50;
let additional_b_key_1 = "curlew";
let additional_b_val_1 = 500;
let primary_parent_spec = fdf::ParentSpec {
bind_rules: primary_bind_rules,
properties: vec![make_property(
fdf::NodePropertyKey::StringValue(primary_key_1.to_string()),
fdf::NodePropertyValue::StringValue(primary_val_1.to_string()),
)],
};
let primary_node_inst = vec![SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::Key(primary_key_1.to_string(), ValueType::Str),
rhs: Symbol::StringValue(primary_val_1.to_string()),
},
}];
let additional_parent_spec_a = fdf::ParentSpec {
bind_rules: additional_bind_rules_1,
properties: vec![make_property(
fdf::NodePropertyKey::IntValue(additional_a_key_1),
fdf::NodePropertyValue::IntValue(additional_a_val_1),
)],
};
let additional_node_a_inst = vec![
SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::DeprecatedKey(additional_a_key_1),
rhs: Symbol::NumberValue(additional_a_val_1.clone().into()),
},
},
SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfEqual {
lhs: Symbol::Key("NA".to_string(), ValueType::Number),
rhs: Symbol::NumberValue(500),
},
},
];
let additional_parent_spec_b = fdf::ParentSpec {
bind_rules: additional_bind_rules_2,
properties: vec![make_property(
fdf::NodePropertyKey::StringValue(additional_b_key_1.to_string()),
fdf::NodePropertyValue::IntValue(additional_b_val_1),
)],
};
let additional_node_b_inst = vec![SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::Key(additional_b_key_1.to_string(), ValueType::Number),
rhs: Symbol::NumberValue(additional_b_val_1.clone().into()),
},
}];
let composite_driver = create_driver_with_rules(
(TEST_PRIMARY_NAME, primary_node_inst),
vec![
(TEST_ADDITIONAL_A_NAME, additional_node_a_inst),
(TEST_ADDITIONAL_B_NAME, additional_node_b_inst),
],
vec![],
);
let nodes =
Some(vec![primary_parent_spec, additional_parent_spec_b, additional_parent_spec_a]);
let composite_spec = fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: nodes.clone(),
..Default::default()
};
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager
.add_composite_node_spec(composite_spec.clone(), vec![&composite_driver])
);
assert_eq!(1, composite_node_spec_manager.get_specs(None).len());
assert_eq!(0, composite_node_spec_manager.get_specs(Some("not_there".to_string())).len());
let specs = composite_node_spec_manager.get_specs(Some("test_spec".to_string()));
assert_eq!(1, specs.len());
let composite_node_spec = &specs[0];
let expected_spec = fdf::CompositeInfo {
spec: Some(composite_spec.clone()),
matched_driver: Some(fdf::CompositeDriverMatch {
composite_driver: Some(fdf::CompositeDriverInfo {
composite_name: Some(TEST_DEVICE_NAME.to_string()),
driver_info: Some(composite_driver.clone().create_driver_info(false)),
..Default::default()
}),
parent_names: Some(vec![
TEST_PRIMARY_NAME.to_string(),
TEST_ADDITIONAL_B_NAME.to_string(),
TEST_ADDITIONAL_A_NAME.to_string(),
]),
primary_parent_index: Some(0),
..Default::default()
}),
..Default::default()
};
let expected_spec_stripped_parents = fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec.clone()))),
matched_driver: Some(fdf::CompositeDriverMatch {
composite_driver: Some(fdf::CompositeDriverInfo {
composite_name: Some(TEST_DEVICE_NAME.to_string()),
driver_info: Some(composite_driver.clone().create_driver_info(false)),
..Default::default()
}),
parent_names: Some(vec![
TEST_PRIMARY_NAME.to_string(),
TEST_ADDITIONAL_B_NAME.to_string(),
TEST_ADDITIONAL_A_NAME.to_string(),
]),
primary_parent_index: Some(0),
..Default::default()
}),
..Default::default()
};
assert_eq!(&expected_spec, composite_node_spec);
// Match additional node A, the last node in the spec at index 2.
let mut device_properties_1: DeviceProperties = HashMap::new();
device_properties_1.insert(PropertyKey::NumberKey(1), Symbol::NumberValue(10));
let expected_parent = fdf::CompositeParent {
composite: Some(expected_spec_stripped_parents.clone()),
index: Some(2),
..Default::default()
};
assert_eq!(
Some(fdi::MatchDriverResult::CompositeParents(vec![expected_parent])),
composite_node_spec_manager.match_parent_specs(&device_properties_1)
);
}
#[fasync::run_singlethreaded(test)]
async fn test_composite_with_rearranged_primary_node() {
let primary_bind_rules = vec![make_accept(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(200),
)];
let additional_bind_rules_1 = vec![make_accept(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(10),
)];
let additional_bind_rules_2 = vec![make_accept(
fdf::NodePropertyKey::IntValue(10),
fdf::NodePropertyValue::BoolValue(true),
)];
let primary_key_1 = "whimbrel";
let primary_val_1 = "sanderling";
let additional_a_key_1 = 100;
let additional_a_val_1 = 50;
let additional_b_key_1 = "curlew";
let additional_b_val_1 = 500;
let primary_parent_spec = fdf::ParentSpec {
bind_rules: primary_bind_rules,
properties: vec![make_property(
fdf::NodePropertyKey::StringValue(primary_key_1.to_string()),
fdf::NodePropertyValue::StringValue(primary_val_1.to_string()),
)],
};
let primary_node_inst = vec![SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::Key(primary_key_1.to_string(), ValueType::Str),
rhs: Symbol::StringValue(primary_val_1.to_string()),
},
}];
let additional_parent_spec_a = fdf::ParentSpec {
bind_rules: additional_bind_rules_1,
properties: vec![make_property(
fdf::NodePropertyKey::IntValue(additional_a_key_1),
fdf::NodePropertyValue::IntValue(additional_a_val_1),
)],
};
let additional_node_a_inst = vec![
SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::DeprecatedKey(additional_a_key_1),
rhs: Symbol::NumberValue(additional_a_val_1.clone().into()),
},
},
SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfEqual {
lhs: Symbol::Key("NA".to_string(), ValueType::Number),
rhs: Symbol::NumberValue(500),
},
},
];
let additional_parent_spec_b = fdf::ParentSpec {
bind_rules: additional_bind_rules_2,
properties: vec![make_property(
fdf::NodePropertyKey::StringValue(additional_b_key_1.to_string()),
fdf::NodePropertyValue::IntValue(additional_b_val_1),
)],
};
let additional_node_b_inst = vec![SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::Key(additional_b_key_1.to_string(), ValueType::Number),
rhs: Symbol::NumberValue(additional_b_val_1.clone().into()),
},
}];
let composite_driver = create_driver_with_rules(
(TEST_PRIMARY_NAME, primary_node_inst),
vec![
(TEST_ADDITIONAL_A_NAME, additional_node_a_inst),
(TEST_ADDITIONAL_B_NAME, additional_node_b_inst),
],
vec![],
);
let nodes =
Some(vec![additional_parent_spec_b, additional_parent_spec_a, primary_parent_spec]);
let composite_spec = fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: nodes.clone(),
..Default::default()
};
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager
.add_composite_node_spec(composite_spec.clone(), vec![&composite_driver])
);
assert_eq!(1, composite_node_spec_manager.get_specs(None).len());
assert_eq!(0, composite_node_spec_manager.get_specs(Some("not_there".to_string())).len());
let specs = composite_node_spec_manager.get_specs(Some("test_spec".to_string()));
assert_eq!(1, specs.len());
let composite_node_spec = &specs[0];
let expected_spec = fdf::CompositeInfo {
spec: Some(composite_spec.clone()),
matched_driver: Some(fdf::CompositeDriverMatch {
composite_driver: Some(fdf::CompositeDriverInfo {
composite_name: Some(TEST_DEVICE_NAME.to_string()),
driver_info: Some(composite_driver.clone().create_driver_info(false)),
..Default::default()
}),
parent_names: Some(vec![
TEST_ADDITIONAL_B_NAME.to_string(),
TEST_ADDITIONAL_A_NAME.to_string(),
TEST_PRIMARY_NAME.to_string(),
]),
primary_parent_index: Some(2),
..Default::default()
}),
..Default::default()
};
let expected_spec_stripped_parents = fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec.clone()))),
matched_driver: Some(fdf::CompositeDriverMatch {
composite_driver: Some(fdf::CompositeDriverInfo {
composite_name: Some(TEST_DEVICE_NAME.to_string()),
driver_info: Some(composite_driver.clone().create_driver_info(false)),
..Default::default()
}),
parent_names: Some(vec![
TEST_ADDITIONAL_B_NAME.to_string(),
TEST_ADDITIONAL_A_NAME.to_string(),
TEST_PRIMARY_NAME.to_string(),
]),
primary_parent_index: Some(2),
..Default::default()
}),
..Default::default()
};
assert_eq!(&expected_spec, composite_node_spec);
// Match additional node A, the last node in the spec at index 2.
let mut device_properties_1: DeviceProperties = HashMap::new();
device_properties_1.insert(PropertyKey::NumberKey(1), Symbol::NumberValue(10));
let expected_parent = fdf::CompositeParent {
composite: Some(expected_spec_stripped_parents.clone()),
index: Some(1),
..Default::default()
};
assert_eq!(
Some(fdi::MatchDriverResult::CompositeParents(vec![expected_parent])),
composite_node_spec_manager.match_parent_specs(&device_properties_1)
);
}
#[fasync::run_singlethreaded(test)]
async fn test_composite_with_optional_match_without_optional() {
let primary_bind_rules = vec![make_accept(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(200),
)];
let additional_bind_rules_1 = vec![make_accept(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(10),
)];
let additional_bind_rules_2 = vec![make_accept(
fdf::NodePropertyKey::IntValue(10),
fdf::NodePropertyValue::BoolValue(true),
)];
let primary_key_1 = "whimbrel";
let primary_val_1 = "sanderling";
let additional_a_key_1 = 100;
let additional_a_val_1 = 50;
let additional_b_key_1 = "curlew";
let additional_b_val_1 = 500;
let optional_a_key_1 = 200;
let optional_a_val_1: u32 = 10;
let primary_parent_spec = fdf::ParentSpec {
bind_rules: primary_bind_rules,
properties: vec![make_property(
fdf::NodePropertyKey::StringValue(primary_key_1.to_string()),
fdf::NodePropertyValue::StringValue(primary_val_1.to_string()),
)],
};
let primary_node_inst = vec![SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::Key(primary_key_1.to_string(), ValueType::Str),
rhs: Symbol::StringValue(primary_val_1.to_string()),
},
}];
let additional_parent_spec_a = fdf::ParentSpec {
bind_rules: additional_bind_rules_1,
properties: vec![make_property(
fdf::NodePropertyKey::IntValue(additional_a_key_1),
fdf::NodePropertyValue::IntValue(additional_a_val_1),
)],
};
let additional_node_a_inst = vec![
SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::DeprecatedKey(additional_a_key_1),
rhs: Symbol::NumberValue(additional_a_val_1.clone().into()),
},
},
SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfEqual {
lhs: Symbol::Key("NA".to_string(), ValueType::Number),
rhs: Symbol::NumberValue(500),
},
},
];
let additional_parent_spec_b = fdf::ParentSpec {
bind_rules: additional_bind_rules_2,
properties: vec![make_property(
fdf::NodePropertyKey::StringValue(additional_b_key_1.to_string()),
fdf::NodePropertyValue::IntValue(additional_b_val_1),
)],
};
let additional_node_b_inst = vec![SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::Key(additional_b_key_1.to_string(), ValueType::Number),
rhs: Symbol::NumberValue(additional_b_val_1.clone().into()),
},
}];
let optional_node_a_inst = vec![
SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::DeprecatedKey(optional_a_key_1),
rhs: Symbol::NumberValue(optional_a_val_1.clone().into()),
},
},
SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfEqual {
lhs: Symbol::Key("NA".to_string(), ValueType::Number),
rhs: Symbol::NumberValue(500),
},
},
];
let composite_driver = create_driver_with_rules(
(TEST_PRIMARY_NAME, primary_node_inst),
vec![
(TEST_ADDITIONAL_A_NAME, additional_node_a_inst),
(TEST_ADDITIONAL_B_NAME, additional_node_b_inst),
],
vec![(TEST_OPTIONAL_NAME, optional_node_a_inst)],
);
let composite_spec = fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: Some(vec![
primary_parent_spec,
additional_parent_spec_b,
additional_parent_spec_a,
]),
..Default::default()
};
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager
.add_composite_node_spec(composite_spec.clone(), vec![&composite_driver])
);
// Match additional node A, the last node in the spec at index 2.
let mut device_properties_1: DeviceProperties = HashMap::new();
device_properties_1.insert(PropertyKey::NumberKey(1), Symbol::NumberValue(10));
let expected_parent = fdf::CompositeParent {
composite: Some(fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec.clone()))),
matched_driver: Some(fdf::CompositeDriverMatch {
composite_driver: Some(fdf::CompositeDriverInfo {
composite_name: Some(TEST_DEVICE_NAME.to_string()),
driver_info: Some(composite_driver.clone().create_driver_info(false)),
..Default::default()
}),
parent_names: Some(vec![
TEST_PRIMARY_NAME.to_string(),
TEST_ADDITIONAL_B_NAME.to_string(),
TEST_ADDITIONAL_A_NAME.to_string(),
]),
primary_parent_index: Some(0),
..Default::default()
}),
..Default::default()
}),
index: Some(2),
..Default::default()
};
assert_eq!(
Some(fdi::MatchDriverResult::CompositeParents(vec![expected_parent])),
composite_node_spec_manager.match_parent_specs(&device_properties_1)
);
}
#[fasync::run_singlethreaded(test)]
async fn test_composite_with_optional_match_with_optional() {
let primary_bind_rules = vec![make_accept(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(200),
)];
let additional_bind_rules_1 = vec![make_accept(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(10),
)];
let additional_bind_rules_2 = vec![make_accept(
fdf::NodePropertyKey::IntValue(10),
fdf::NodePropertyValue::BoolValue(true),
)];
let optional_bind_rules_1 = vec![make_accept(
fdf::NodePropertyKey::IntValue(1000),
fdf::NodePropertyValue::IntValue(1000),
)];
let primary_key_1 = "whimbrel";
let primary_val_1 = "sanderling";
let additional_a_key_1 = 100;
let additional_a_val_1 = 50;
let additional_b_key_1 = "curlew";
let additional_b_val_1 = 500;
let optional_a_key_1 = 200;
let optional_a_val_1 = 10;
let primary_parent_spec = fdf::ParentSpec {
bind_rules: primary_bind_rules,
properties: vec![make_property(
fdf::NodePropertyKey::StringValue(primary_key_1.to_string()),
fdf::NodePropertyValue::StringValue(primary_val_1.to_string()),
)],
};
let primary_node_inst = vec![SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::Key(primary_key_1.to_string(), ValueType::Str),
rhs: Symbol::StringValue(primary_val_1.to_string()),
},
}];
let additional_parent_spec_a = fdf::ParentSpec {
bind_rules: additional_bind_rules_1,
properties: vec![make_property(
fdf::NodePropertyKey::IntValue(additional_a_key_1),
fdf::NodePropertyValue::IntValue(additional_a_val_1),
)],
};
let additional_node_a_inst = vec![
SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::DeprecatedKey(additional_a_key_1),
rhs: Symbol::NumberValue(additional_a_val_1.clone().into()),
},
},
SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfEqual {
lhs: Symbol::Key("NA".to_string(), ValueType::Number),
rhs: Symbol::NumberValue(500),
},
},
];
let additional_parent_spec_b = fdf::ParentSpec {
bind_rules: additional_bind_rules_2,
properties: vec![make_property(
fdf::NodePropertyKey::StringValue(additional_b_key_1.to_string()),
fdf::NodePropertyValue::IntValue(additional_b_val_1),
)],
};
let additional_node_b_inst = vec![SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::Key(additional_b_key_1.to_string(), ValueType::Number),
rhs: Symbol::NumberValue(additional_b_val_1.clone().into()),
},
}];
let optional_node_parent_a = fdf::ParentSpec {
bind_rules: optional_bind_rules_1,
properties: vec![make_property(
fdf::NodePropertyKey::IntValue(optional_a_key_1),
fdf::NodePropertyValue::IntValue(optional_a_val_1),
)],
};
let optional_node_a_inst = vec![
SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::DeprecatedKey(optional_a_key_1),
rhs: Symbol::NumberValue(optional_a_val_1.clone().into()),
},
},
SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfEqual {
lhs: Symbol::Key("NA".to_string(), ValueType::Number),
rhs: Symbol::NumberValue(500),
},
},
];
let composite_driver = create_driver_with_rules(
(TEST_PRIMARY_NAME, primary_node_inst),
vec![
(TEST_ADDITIONAL_A_NAME, additional_node_a_inst),
(TEST_ADDITIONAL_B_NAME, additional_node_b_inst),
],
vec![(TEST_OPTIONAL_NAME, optional_node_a_inst)],
);
let composite_spec = fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: Some(vec![
primary_parent_spec,
additional_parent_spec_b,
optional_node_parent_a,
additional_parent_spec_a,
]),
..Default::default()
};
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager
.add_composite_node_spec(composite_spec.clone(), vec![&composite_driver])
);
// Match additional node A, the last node in the spec at index 3.
let mut device_properties_1: DeviceProperties = HashMap::new();
device_properties_1.insert(PropertyKey::NumberKey(1), Symbol::NumberValue(10));
let expected_composite = fdf::CompositeInfo {
spec: Some(strip_parents_from_spec(&Some(composite_spec.clone()))),
matched_driver: Some(fdf::CompositeDriverMatch {
composite_driver: Some(fdf::CompositeDriverInfo {
composite_name: Some(TEST_DEVICE_NAME.to_string()),
driver_info: Some(composite_driver.clone().create_driver_info(false)),
..Default::default()
}),
parent_names: Some(vec![
TEST_PRIMARY_NAME.to_string(),
TEST_ADDITIONAL_B_NAME.to_string(),
TEST_OPTIONAL_NAME.to_string(),
TEST_ADDITIONAL_A_NAME.to_string(),
]),
primary_parent_index: Some(0),
..Default::default()
}),
..Default::default()
};
let expected_parent = fdf::CompositeParent {
composite: Some(expected_composite.clone()),
index: Some(3),
..Default::default()
};
assert_eq!(
Some(fdi::MatchDriverResult::CompositeParents(vec![expected_parent])),
composite_node_spec_manager.match_parent_specs(&device_properties_1)
);
// Match optional node A, the second to last node in the spec at index 2.
let mut device_properties_1: DeviceProperties = HashMap::new();
device_properties_1.insert(PropertyKey::NumberKey(1000), Symbol::NumberValue(1000));
let expected_parent_2 = fdf::CompositeParent {
composite: Some(expected_composite.clone()),
index: Some(2),
..Default::default()
};
assert_eq!(
Some(fdi::MatchDriverResult::CompositeParents(vec![expected_parent_2])),
composite_node_spec_manager.match_parent_specs(&device_properties_1)
);
}
#[fasync::run_singlethreaded(test)]
async fn test_composite_mismatch() {
let primary_bind_rules = vec![make_accept(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(200),
)];
let additional_bind_rules_1 = vec![make_accept(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(10),
)];
let additional_bind_rules_2 = vec![make_accept(
fdf::NodePropertyKey::IntValue(10),
fdf::NodePropertyValue::BoolValue(false),
)];
let primary_key_1 = "whimbrel";
let primary_val_1 = "sanderling";
let additional_a_key_1 = 100;
let additional_a_val_1 = 50;
let additional_b_key_1 = "curlew";
let additional_b_val_1 = 500;
let primary_node_inst = vec![SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::Key(primary_key_1.to_string(), ValueType::Str),
rhs: Symbol::StringValue(primary_val_1.to_string()),
},
}];
let primary_parent_spec = fdf::ParentSpec {
bind_rules: primary_bind_rules,
properties: vec![make_property(
fdf::NodePropertyKey::StringValue(primary_key_1.to_string()),
fdf::NodePropertyValue::StringValue(primary_val_1.to_string()),
)],
};
let additional_node_a_inst = vec![
SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::Key(additional_b_key_1.to_string(), ValueType::Number),
rhs: Symbol::NumberValue(additional_b_val_1.clone().into()),
},
},
SymbolicInstructionInfo {
location: None,
// This does not exist in our properties so we expect it to not match.
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::Key("NA".to_string(), ValueType::Number),
rhs: Symbol::NumberValue(500),
},
},
];
let additional_parent_spec_a = fdf::ParentSpec {
bind_rules: additional_bind_rules_1,
properties: vec![make_property(
fdf::NodePropertyKey::StringValue(additional_b_key_1.to_string()),
fdf::NodePropertyValue::IntValue(additional_b_val_1),
)],
};
let additional_node_b_inst = vec![SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::DeprecatedKey(additional_a_key_1.clone()),
rhs: Symbol::NumberValue(additional_a_val_1.clone().into()),
},
}];
let additional_parent_spec_b = fdf::ParentSpec {
bind_rules: additional_bind_rules_2,
properties: vec![make_property(
fdf::NodePropertyKey::IntValue(additional_a_key_1),
fdf::NodePropertyValue::IntValue(additional_a_val_1),
)],
};
let composite_driver = create_driver_with_rules(
(TEST_PRIMARY_NAME, primary_node_inst),
vec![
(TEST_ADDITIONAL_A_NAME, additional_node_a_inst),
(TEST_ADDITIONAL_B_NAME, additional_node_b_inst),
],
vec![],
);
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(
fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: Some(vec![
primary_parent_spec,
additional_parent_spec_a,
additional_parent_spec_b
]),
..Default::default()
},
vec![&composite_driver]
)
);
}
#[fasync::run_singlethreaded(test)]
async fn test_valid_name() {
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
let node = fdf::ParentSpec {
bind_rules: vec![make_accept(
fdf::NodePropertyKey::StringValue("wrybill".to_string()),
fdf::NodePropertyValue::IntValue(200),
)],
properties: vec![make_property(
fdf::NodePropertyKey::StringValue("dotteral".to_string()),
fdf::NodePropertyValue::StringValue("wrybill".to_string()),
)],
};
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(
fdf::CompositeNodeSpec {
name: Some("test-spec".to_string()),
parents: Some(vec![node.clone()]),
..Default::default()
},
vec![]
)
);
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(
fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: Some(vec![node]),
..Default::default()
},
vec![]
)
);
}
#[fasync::run_singlethreaded(test)]
async fn test_invalid_name() {
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
let node = fdf::ParentSpec {
bind_rules: vec![make_accept(
fdf::NodePropertyKey::StringValue("wrybill".to_string()),
fdf::NodePropertyValue::IntValue(200),
)],
properties: vec![make_property(
fdf::NodePropertyKey::StringValue("dotteral".to_string()),
fdf::NodePropertyValue::IntValue(100),
)],
};
assert_eq!(
Err(Status::INVALID_ARGS.into_raw()),
composite_node_spec_manager.add_composite_node_spec(
fdf::CompositeNodeSpec {
name: Some("test/spec".to_string()),
parents: Some(vec![node.clone()]),
..Default::default()
},
vec![]
)
);
assert_eq!(
Err(Status::INVALID_ARGS.into_raw()),
composite_node_spec_manager.add_composite_node_spec(
fdf::CompositeNodeSpec {
name: Some("test:spec".to_string()),
parents: Some(vec![node]),
..Default::default()
},
vec![]
)
);
}
#[fasync::run_singlethreaded(test)]
async fn test_rebind() {
let primary_bind_rules = vec![make_accept(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(200),
)];
let primary_key_1 = "whimbrel";
let primary_val_1 = "sanderling";
let primary_parent_spec = fdf::ParentSpec {
bind_rules: primary_bind_rules,
properties: vec![make_property(
fdf::NodePropertyKey::StringValue(primary_key_1.to_string()),
fdf::NodePropertyValue::StringValue(primary_val_1.to_string()),
)],
};
let primary_node_inst = vec![SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::Key(primary_key_1.to_string(), ValueType::Str),
rhs: Symbol::StringValue(primary_val_1.to_string()),
},
}];
let composite_driver = create_driver_with_rules(
(TEST_PRIMARY_NAME, primary_node_inst.clone()),
vec![],
vec![],
);
let nodes = Some(vec![primary_parent_spec]);
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(
fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: nodes.clone(),
..Default::default()
},
vec![&composite_driver]
)
);
let rebind_driver = create_driver(
"rebind_composite".to_string(),
(TEST_PRIMARY_NAME, primary_node_inst),
vec![],
vec![],
);
assert!(composite_node_spec_manager
.rebind("test_spec".to_string(), vec![&rebind_driver])
.is_ok());
assert_eq!(
fdf::CompositeDriverInfo {
composite_name: Some("rebind_composite".to_string()),
driver_info: Some(rebind_driver.clone().create_driver_info(false)),
..Default::default()
},
composite_node_spec_manager
.spec_list
.get("test_spec")
.unwrap()
.matched_driver
.as_ref()
.unwrap()
.composite_driver
.as_ref()
.unwrap()
.clone()
);
}
#[fasync::run_singlethreaded(test)]
async fn test_rebind_no_match() {
let primary_bind_rules = vec![make_accept(
fdf::NodePropertyKey::IntValue(1),
fdf::NodePropertyValue::IntValue(200),
)];
let primary_key_1 = "whimbrel";
let primary_val_1 = "sanderling";
let primary_parent_spec = fdf::ParentSpec {
bind_rules: primary_bind_rules,
properties: vec![make_property(
fdf::NodePropertyKey::StringValue(primary_key_1.to_string()),
fdf::NodePropertyValue::StringValue(primary_val_1.to_string()),
)],
};
let primary_node_inst = vec![SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::Key(primary_key_1.to_string(), ValueType::Str),
rhs: Symbol::StringValue(primary_val_1.to_string()),
},
}];
let composite_driver =
create_driver_with_rules((TEST_PRIMARY_NAME, primary_node_inst), vec![], vec![]);
let nodes = Some(vec![primary_parent_spec]);
let mut composite_node_spec_manager = CompositeNodeSpecManager::new();
assert_eq!(
Ok(()),
composite_node_spec_manager.add_composite_node_spec(
fdf::CompositeNodeSpec {
name: Some("test_spec".to_string()),
parents: nodes.clone(),
..Default::default()
},
vec![&composite_driver]
)
);
// Create a composite driver for rebinding that won't match to the spec.
let rebind_primary_node_inst = vec![SymbolicInstructionInfo {
location: None,
instruction: SymbolicInstruction::AbortIfNotEqual {
lhs: Symbol::Key("unmatched".to_string(), ValueType::Bool),
rhs: Symbol::BoolValue(false),
},
}];
let rebind_driver = create_driver(
"rebind_composite".to_string(),
(TEST_PRIMARY_NAME, rebind_primary_node_inst),
vec![],
vec![],
);
assert!(composite_node_spec_manager
.rebind("test_spec".to_string(), vec![&rebind_driver])
.is_ok());
assert_eq!(
None,
composite_node_spec_manager.spec_list.get("test_spec").unwrap().matched_driver
);
}
}