blob: 341d068edb0089d8dd63a92ec219295835251ae1 [file] [log] [blame]
// Copyright 2023 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 anyhow::{anyhow, Result};
use fidl_fuchsia_driver_development as fdd;
use futures::channel::mpsc;
use futures::StreamExt;
use std::collections::{HashMap, HashSet};
// Wait for the events from the |nodes| to be received. Updates the entries to be Some.
pub async fn wait_for_nodes(
nodes: &mut HashMap<String, Option<Option<u64>>>,
receiver: &mut mpsc::Receiver<(String, String)>,
) -> Result<()> {
while nodes.values().any(|&x| x.is_none()) {
let (from_node, _) = receiver.next().await.ok_or(anyhow!("Receiver failed"))?;
if !nodes.contains_key(&from_node) {
return Err(anyhow!("Couldn't find node '{}' in 'nodes'.", from_node.to_string()));
}
nodes.entry(from_node).and_modify(|x| {
*x = Some(None);
});
}
Ok(())
}
// Validates the host koids given the device infos.
// Performs the following:
// - Stores the host koid for nodes in changed_or_new as those are expected to be new/changed.
// - Validate bound nodes are not in should_not_exist
// - Validate bound nodes have the same koid as the most recent item in previous.
// - Validate that items in changed_or_new have been changed from the most recent item in previous
// if they are not new.
pub async fn validate_host_koids(
test_stage_name: &str,
device_infos: Vec<fdd::NodeInfo>,
changed_or_new: &mut HashMap<String, Option<Option<u64>>>,
previous: Vec<&HashMap<String, Option<Option<u64>>>>,
should_not_exist: Option<&HashSet<String>>,
) -> Result<()> {
for dev in &device_infos {
let key = dev.moniker.clone().unwrap().split(".").last().unwrap().to_string();
// Items in changed_or_new are expected to be different so just save that info and move on.
if changed_or_new.contains_key(&key) {
changed_or_new.entry(key).and_modify(|x| {
*x = Some(dev.driver_host_koid);
});
continue;
}
// Skip comparison and should_not_exist check as the koid is not valid when its unbound.
if matches!(dev.bound_driver_url.as_deref(), Some("unbound")) {
continue;
}
// Error if the item is in should not exist.
if let Some(should_not_exist_value) = &should_not_exist {
if should_not_exist_value.contains(&key) {
return Err(anyhow!(
"Found node that should not exist after {}: '{}'.",
test_stage_name,
key
));
}
}
// Go through the previous items (which should come in from most to least recent order)
// and make sure this matches the most recent instance.
for prev in &previous {
if let Some(prev_koid) = prev.get(&key) {
match prev_koid {
Some(prev_koid_value) => {
if *prev_koid_value != dev.driver_host_koid {
return Err(anyhow!(
"koid should not have changed for node '{}' after {}.",
key,
test_stage_name
));
}
break;
}
None => {
// This is not possible as things are today.
// The values in the entries cannot be None since in wait_for_nodes we have
// waited for all of them to not be None.
return Err(anyhow!("prev koid not available after."));
}
}
}
}
}
// Now we can make sure those items in changed_or_new are different than their most recent
// previous item. Skipping ones that are not seen in previous.
for (key, changed_or_new_node_entry) in changed_or_new {
// First find the most recent previous koid if one exists.
let mut koid_before: Option<&Option<u64>> = None;
for prev in &previous {
if let Some(prev_koid) = prev.get(key) {
match prev_koid {
Some(prev_koid_value) => {
koid_before = Some(prev_koid_value);
break;
}
None => {
// This is not possible as things are today.
// The values in the entries cannot be None since in wait_for_nodes we have
// waited for all of them to not be None.
return Err(anyhow!("previous map entry cannot have None outer option."));
}
}
}
}
// Now compare it with current to make sure it is different.
match koid_before {
Some(koid_before) => match koid_before {
Some(koid_before) => match changed_or_new_node_entry {
Some(koid_after) => match koid_after {
Some(koid_after) => {
// The node had a koid in both current and previous. Ensure that the
// koid value is different.
if koid_before == koid_after {
return Err(anyhow!(
"koid should have changed for node '{}' after {}.",
key,
test_stage_name
));
}
}
None => {
// The current node doesn't contain a koid.
// This can happen if the node is a composite parent, or if it is unbound.
continue;
}
},
None => {
// This is not possible as things are today.
// The values in the entries cannot be None since in wait_for_nodes we have
// waited for all of them to not be None.
return Err(anyhow!("changed_or_new_node entry cannot be None."));
}
},
None => {
// This node existed in a previous item, but it didn't contain a koid.
// This can happen if the node was a composite parent, or if it was unbound.
continue;
}
},
None => {
// This is a new node and it doesn't exist in previous.
continue;
}
}
}
Ok(())
}