fuchsia_inspect_contrib/nodes/
list.rsuse fuchsia_inspect::Node;
use std::collections::VecDeque;
#[derive(Debug)]
pub struct BoundedListNode {
node: Node,
index: usize,
capacity: usize,
items: VecDeque<Node>,
}
impl BoundedListNode {
pub fn new(node: Node, capacity: usize) -> Self {
Self {
node,
index: 0,
capacity: std::cmp::max(capacity, 1),
items: VecDeque::with_capacity(capacity),
}
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.len() == 0
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn add_entry<F>(&mut self, initialize: F) -> &Node
where
F: FnOnce(&Node),
{
if self.items.len() >= self.capacity {
self.items.pop_front();
}
let entry_node = self.node.atomic_update(|node| {
let child = node.create_child(self.index.to_string());
initialize(&child);
child
});
self.items.push_back(entry_node);
self.index += 1;
self.items.back().unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
use diagnostics_assertions::assert_data_tree;
use fuchsia_inspect::reader::{self, ReaderError};
use fuchsia_inspect::Inspector;
use std::sync::mpsc;
#[fuchsia::test]
fn test_bounded_list_node_basic() {
let inspector = Inspector::default();
let list_node = inspector.root().create_child("list_node");
let mut list_node = BoundedListNode::new(list_node, 3);
assert_eq!(list_node.capacity(), 3);
assert_eq!(list_node.len(), 0);
let _ = list_node.add_entry(|_| {});
assert_eq!(list_node.len(), 1);
assert_data_tree!(inspector, root: { list_node: { "0": {} } });
let _ = list_node.add_entry(|_| {});
assert_eq!(list_node.len(), 2);
assert_data_tree!(inspector, root: { list_node: { "0": {}, "1": {} } });
}
#[fuchsia::test]
fn test_bounded_list_node_eviction() {
let inspector = Inspector::default();
let list_node = inspector.root().create_child("list_node");
let mut list_node = BoundedListNode::new(list_node, 3);
let _ = list_node.add_entry(|_| {});
let _ = list_node.add_entry(|_| {});
let _ = list_node.add_entry(|_| {});
assert_data_tree!(inspector, root: { list_node: { "0": {}, "1": {}, "2": {} } });
assert_eq!(list_node.len(), 3);
let _ = list_node.add_entry(|_| {});
assert_data_tree!(inspector, root: { list_node: { "1": {}, "2": {}, "3": {} } });
assert_eq!(list_node.len(), 3);
let _ = list_node.add_entry(|_| {});
assert_data_tree!(inspector, root: { list_node: { "2": {}, "3": {}, "4": {} } });
assert_eq!(list_node.len(), 3);
}
#[fuchsia::test]
fn test_bounded_list_node_specified_zero_capacity() {
let inspector = Inspector::default();
let list_node = inspector.root().create_child("list_node");
let mut list_node = BoundedListNode::new(list_node, 0);
let _ = list_node.add_entry(|_| {});
assert_data_tree!(inspector, root: { list_node: { "0": {} } });
let _ = list_node.add_entry(|_| {});
assert_data_tree!(inspector, root: { list_node: { "1": {} } });
}
#[fuchsia::test]
fn test_bounded_list_node_holds_its_values() {
let inspector = Inspector::default();
let list_node = inspector.root().create_child("list_node");
let mut list_node = BoundedListNode::new(list_node, 3);
{
let node_writer = list_node.add_entry(|_| {});
node_writer.record_string("str_key", "str_value");
node_writer.record_child("child", |child| child.record_int("int_key", 2));
} assert_data_tree!(inspector, root: {
list_node: {
"0": {
str_key: "str_value",
child: {
int_key: 2i64,
}
}
}
});
}
#[fuchsia::test]
async fn add_entry_is_atomic() {
let inspector = Inspector::default();
let list_node = inspector.root().create_child("list_node");
let mut list_node = BoundedListNode::new(list_node, 3);
let (sender, receiver) = mpsc::channel();
let (sender2, receiver2) = mpsc::channel();
let t = std::thread::spawn(move || {
list_node.add_entry(|node| {
node.record_string("key1", "value1");
sender.send(()).unwrap();
receiver2.recv().unwrap();
node.record_string("key2", "value2");
});
list_node
});
receiver.recv().unwrap();
assert_matches!(reader::read(&inspector).await, Err(ReaderError::InconsistentSnapshot));
sender2.send(()).unwrap();
let _list_node = t.join().unwrap();
assert_data_tree!(inspector, root: {
list_node: {
"0": {
key1: "value1",
key2: "value2",
}
}
});
}
}