fuchsia_inspect_contrib/nodes/
list.rs1use fuchsia_inspect::Node;
6use std::collections::VecDeque;
7
8#[derive(Debug)]
16pub struct BoundedListNode {
17 node: Node,
18 index: usize,
19 capacity: usize,
20 items: VecDeque<Node>,
21}
22
23impl BoundedListNode {
24 pub fn new(node: Node, capacity: usize) -> Self {
26 Self {
27 node,
28 index: 0,
29 capacity: std::cmp::max(capacity, 1),
30 items: VecDeque::with_capacity(capacity),
31 }
32 }
33
34 pub fn len(&self) -> usize {
36 self.items.len()
37 }
38
39 pub fn is_empty(&self) -> bool {
41 self.items.len() == 0
42 }
43
44 pub fn capacity(&self) -> usize {
46 self.capacity
47 }
48
49 pub fn add_entry<F>(&mut self, initialize: F) -> &Node
58 where
59 F: FnOnce(&Node),
60 {
61 if self.items.len() >= self.capacity {
62 self.items.pop_front();
63 }
64
65 let entry_node = self.node.atomic_update(|node| {
66 let child = node.create_child(self.index.to_string());
67 initialize(&child);
68 child
69 });
70 self.items.push_back(entry_node);
71
72 self.index += 1;
73 self.items.back().unwrap()
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use assert_matches::assert_matches;
81 use diagnostics_assertions::assert_data_tree;
82 use fuchsia_inspect::reader::{self, ReaderError};
83 use fuchsia_inspect::Inspector;
84 use std::sync::mpsc;
85
86 #[fuchsia::test]
87 fn test_bounded_list_node_basic() {
88 let inspector = Inspector::default();
89 let list_node = inspector.root().create_child("list_node");
90 let mut list_node = BoundedListNode::new(list_node, 3);
91 assert_eq!(list_node.capacity(), 3);
92 assert_eq!(list_node.len(), 0);
93 let _ = list_node.add_entry(|_| {});
94 assert_eq!(list_node.len(), 1);
95 assert_data_tree!(inspector, root: { list_node: { "0": {} } });
96 let _ = list_node.add_entry(|_| {});
97 assert_eq!(list_node.len(), 2);
98 assert_data_tree!(inspector, root: { list_node: { "0": {}, "1": {} } });
99 }
100
101 #[fuchsia::test]
102 fn test_bounded_list_node_eviction() {
103 let inspector = Inspector::default();
104 let list_node = inspector.root().create_child("list_node");
105 let mut list_node = BoundedListNode::new(list_node, 3);
106 let _ = list_node.add_entry(|_| {});
107 let _ = list_node.add_entry(|_| {});
108 let _ = list_node.add_entry(|_| {});
109
110 assert_data_tree!(inspector, root: { list_node: { "0": {}, "1": {}, "2": {} } });
111 assert_eq!(list_node.len(), 3);
112
113 let _ = list_node.add_entry(|_| {});
114 assert_data_tree!(inspector, root: { list_node: { "1": {}, "2": {}, "3": {} } });
115 assert_eq!(list_node.len(), 3);
116
117 let _ = list_node.add_entry(|_| {});
118 assert_data_tree!(inspector, root: { list_node: { "2": {}, "3": {}, "4": {} } });
119 assert_eq!(list_node.len(), 3);
120 }
121
122 #[fuchsia::test]
123 fn test_bounded_list_node_specified_zero_capacity() {
124 let inspector = Inspector::default();
125 let list_node = inspector.root().create_child("list_node");
126 let mut list_node = BoundedListNode::new(list_node, 0);
127 let _ = list_node.add_entry(|_| {});
128 assert_data_tree!(inspector, root: { list_node: { "0": {} } });
129 let _ = list_node.add_entry(|_| {});
130 assert_data_tree!(inspector, root: { list_node: { "1": {} } });
131 }
132
133 #[fuchsia::test]
134 fn test_bounded_list_node_holds_its_values() {
135 let inspector = Inspector::default();
136 let list_node = inspector.root().create_child("list_node");
137 let mut list_node = BoundedListNode::new(list_node, 3);
138
139 {
140 let node_writer = list_node.add_entry(|_| {});
141 node_writer.record_string("str_key", "str_value");
142 node_writer.record_child("child", |child| child.record_int("int_key", 2));
143 } assert_data_tree!(inspector, root: {
147 list_node: {
148 "0": {
149 str_key: "str_value",
150 child: {
151 int_key: 2i64,
152 }
153 }
154 }
155 });
156 }
157
158 #[fuchsia::test]
159 async fn add_entry_is_atomic() {
160 let inspector = Inspector::default();
161 let list_node = inspector.root().create_child("list_node");
162 let mut list_node = BoundedListNode::new(list_node, 3);
163
164 let (sender, receiver) = mpsc::channel();
165 let (sender2, receiver2) = mpsc::channel();
166
167 let t = std::thread::spawn(move || {
168 list_node.add_entry(|node| {
169 node.record_string("key1", "value1");
170 sender.send(()).unwrap();
171 receiver2.recv().unwrap();
172 node.record_string("key2", "value2");
173 });
174 list_node
175 });
176
177 receiver.recv().unwrap();
179
180 assert_matches!(reader::read(&inspector).await, Err(ReaderError::InconsistentSnapshot));
182
183 sender2.send(()).unwrap();
185
186 let _list_node = t.join().unwrap();
188
189 assert_data_tree!(inspector, root: {
191 list_node: {
192 "0": {
193 key1: "value1",
194 key2: "value2",
195 }
196 }
197 });
198 }
199}