1use anyhow::{Error, format_err};
6use fidl::endpoints::create_proxy;
7use fidl_fuchsia_inspect::{TreeMarker, TreeNameIteratorMarker, TreeProxy};
8use std::collections::HashMap;
9use std::future::Future;
10use std::pin::Pin;
11use zx::Vmo;
12
13#[derive(Debug)]
16pub struct LazyNode {
17 vmo: Vmo,
18 children: Option<HashMap<String, LazyNode>>,
19}
20
21impl LazyNode {
22 pub fn new(
25 channel: TreeProxy,
26 ) -> Pin<Box<dyn Future<Output = Result<LazyNode, Error>> + Send>> {
27 Box::pin(async move {
28 let fetcher = LazyNodeFetcher::new(channel);
29 let vmo = fetcher.get_vmo().await?;
30 let mut children = HashMap::new();
31 for child_name in fetcher.get_child_names().await?.iter() {
32 let child_channel = fetcher.get_child_tree_channel(child_name).await?;
33 let lazy_node = LazyNode::new(child_channel).await?;
34 children.insert(child_name.to_string(), lazy_node);
35 }
36 Ok(LazyNode { vmo, children: Some(children) })
37 })
38 }
39
40 pub fn vmo(&self) -> &Vmo {
42 &self.vmo
43 }
44
45 pub fn take_children(&mut self) -> Option<HashMap<String, LazyNode>> {
49 self.children.take()
50 }
51}
52
53struct LazyNodeFetcher {
56 channel: TreeProxy,
57}
58
59impl LazyNodeFetcher {
60 fn new(channel: TreeProxy) -> LazyNodeFetcher {
61 LazyNodeFetcher { channel }
62 }
63
64 async fn get_vmo(&self) -> Result<Vmo, Error> {
65 let tree_content = self.channel.get_content().await?;
66 tree_content.buffer.map(|b| b.vmo).ok_or_else(|| format_err!("Failed to fetch VMO."))
67 }
68
69 async fn get_child_names(&self) -> Result<Vec<String>, Error> {
70 let (name_iterator, server_end) = create_proxy::<TreeNameIteratorMarker>();
71 self.channel.list_child_names(server_end)?;
72 let mut names = vec![];
73 loop {
74 let subset = name_iterator.get_next().await?;
75 if subset.is_empty() {
76 return Ok(names);
77 }
78 names.extend(subset);
79 }
80 }
81
82 async fn get_child_tree_channel(&self, name: &str) -> Result<TreeProxy, Error> {
83 let (child_channel, server_end) = create_proxy::<TreeMarker>();
84 self.channel.open_child(name, server_end)?;
85 Ok(child_channel)
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use anyhow::Context;
93 use fidl_fuchsia_inspect::{
94 TreeContent, TreeNameIteratorRequest, TreeNameIteratorRequestStream, TreeRequest,
95 TreeRequestStream,
96 };
97 use fidl_fuchsia_mem::Buffer;
98 use fuchsia_async as fasync;
99 use futures::{TryFutureExt, TryStreamExt};
100 use log::{error, warn};
101 use std::sync::Arc;
102
103 const MAX_TREE_NAME_LIST_SIZE: usize = 1;
104 const SHARED_VMO: &str = "SHARED";
105 const SINGLE_CHILD_ROOT: &str = "SINGLE_CHILD_ROOT";
106 const NAMED_CHILD_NODE: &str = "NAMED_CHILD_NODE";
107 const NAMED_GRANDCHILD_NODE: &str = "NAMED_GRANDCHILD_NODE";
108 const MULTI_CHILD_ROOT: &str = "MULTI_CHILD_ROOT";
109 const ALL_NODES: [&str; 4] =
110 [SINGLE_CHILD_ROOT, NAMED_CHILD_NODE, NAMED_GRANDCHILD_NODE, MULTI_CHILD_ROOT];
111
112 #[fuchsia::test]
113 async fn complete_vmo_tree_gets_read() -> Result<(), Error> {
114 let vmos = Arc::new(TestVmos::new());
115 let tree = spawn_root_tree_server(SINGLE_CHILD_ROOT, Arc::clone(&vmos))?;
116
117 let root_tree = LazyNode::new(tree).await?;
118 let child_tree = root_tree.children.as_ref().unwrap().get(NAMED_CHILD_NODE).unwrap();
119 let grandchild_tree =
120 child_tree.children.as_ref().unwrap().get(NAMED_GRANDCHILD_NODE).unwrap();
121
122 assert_has_size(root_tree.children.as_ref().unwrap(), 1);
123 assert_has_size(child_tree.children.as_ref().unwrap(), 1);
124 assert_has_size(grandchild_tree.children.as_ref().unwrap(), 0);
125 assert_has_vmo(&vmos, SINGLE_CHILD_ROOT, &root_tree);
126 assert_has_vmo(&vmos, NAMED_CHILD_NODE, child_tree);
127 assert_has_vmo(&vmos, NAMED_GRANDCHILD_NODE, grandchild_tree);
128
129 Ok(())
130 }
131
132 #[fuchsia::test]
133 async fn repeatedly_calls_list_children_until_all_names_are_fetched() -> Result<(), Error> {
134 let vmos = Arc::new(TestVmos::new());
135 let tree = spawn_root_tree_server(MULTI_CHILD_ROOT, Arc::clone(&vmos))?;
136 let mut root_tree = LazyNode::new(tree).await?;
137
138 let children = root_tree.take_children().unwrap();
139
140 assert!(root_tree.children.is_none());
141 assert_has_size(&children, child_names(MULTI_CHILD_ROOT).len());
142 for name in child_names(MULTI_CHILD_ROOT).iter() {
143 assert!(children.contains_key(name));
144 }
145
146 Ok(())
147 }
148
149 fn spawn_root_tree_server(tree_name: &str, vmos: Arc<TestVmos>) -> Result<TreeProxy, Error> {
150 let (tree, request_stream) = fidl::endpoints::create_proxy_and_stream::<TreeMarker>();
151 spawn_tree_server(tree_name.to_string(), vmos, request_stream);
152 Ok(tree)
153 }
154
155 fn spawn_tree_server(name: String, vmos: Arc<TestVmos>, stream: TreeRequestStream) {
156 fasync::Task::spawn(async move {
157 handle_request_stream(name, vmos, stream)
158 .await
159 .unwrap_or_else(|err: Error| error!(err:?; "Couldn't run tree server"));
160 })
161 .detach();
162 }
163
164 async fn handle_request_stream(
165 name: String,
166 vmos: Arc<TestVmos>,
167 mut stream: TreeRequestStream,
168 ) -> Result<(), Error> {
169 while let Some(request) = stream.try_next().await.context("Error running tree server")? {
170 match request {
171 TreeRequest::GetContent { responder } => {
172 let vmo = vmos.get(&name);
173 let size = vmo.get_size()?;
174 let content =
175 TreeContent { buffer: Some(Buffer { vmo, size }), ..Default::default() };
176 responder.send(content)?;
177 }
178 TreeRequest::ListChildNames { tree_iterator, .. } => {
179 let request_stream = tree_iterator.into_stream();
180 spawn_tree_name_iterator_server(child_names(&name), request_stream)
181 }
182 TreeRequest::OpenChild { child_name, tree, .. } => {
183 spawn_tree_server(child_name, Arc::clone(&vmos), tree.into_stream())
184 }
185 TreeRequest::_UnknownMethod { ordinal, method_type, .. } => {
186 warn!(ordinal, method_type:?; "Unknown request");
187 }
188 }
189 }
190 Ok(())
191 }
192
193 fn spawn_tree_name_iterator_server(
194 values: Vec<String>,
195 mut stream: TreeNameIteratorRequestStream,
196 ) {
197 fasync::Task::spawn(
198 async move {
199 let mut values_iter = values.into_iter();
200 while let Some(request) =
201 stream.try_next().await.context("Error running tree iterator server")?
202 {
203 match request {
204 TreeNameIteratorRequest::GetNext { responder } => {
205 let result = values_iter
206 .by_ref()
207 .take(MAX_TREE_NAME_LIST_SIZE)
208 .collect::<Vec<String>>();
209 if result.is_empty() {
210 responder.send(&[])?;
211 return Ok(());
212 }
213 responder.send(&result)?;
214 }
215 TreeNameIteratorRequest::_UnknownMethod {
216 ordinal, method_type, ..
217 } => {
218 warn!(ordinal, method_type:?; "Unknown request");
219 }
220 }
221 }
222 Ok(())
223 }
224 .unwrap_or_else(|err: Error| {
225 error!(err:?; "Failed to running tree name iterator server");
226 }),
227 )
228 .detach()
229 }
230
231 struct TestVmos {
232 vmos: HashMap<String, zx::Vmo>,
233 default: zx::Vmo,
234 }
235
236 impl TestVmos {
237 fn new() -> TestVmos {
238 let mut vmos = HashMap::new();
239 vmos.insert(SHARED_VMO.to_string(), create_vmo(SHARED_VMO));
240 for value in ALL_NODES.iter() {
241 let vmo = create_vmo(value);
242 vmos.insert(value.to_string(), vmo);
243 }
244 TestVmos { vmos, default: create_vmo(SHARED_VMO) }
245 }
246
247 fn get(&self, value: &str) -> zx::Vmo {
248 duplicate_vmo(self.vmos.get(value).unwrap_or(&self.default))
249 }
250 }
251
252 fn assert_has_vmo(vmos: &TestVmos, name: &str, node: &LazyNode) {
253 let actual = get_vmo_as_buf(node.vmo()).unwrap();
254 let expected = get_vmo_as_buf(&vmos.get(name)).unwrap();
255 assert_eq!(actual, expected);
256 }
257
258 fn assert_has_size(map: &HashMap<String, LazyNode>, size: usize) {
259 assert_eq!(map.len(), size);
260 }
261
262 fn get_vmo_as_buf(vmo: &Vmo) -> Result<Vec<u8>, Error> {
263 let size = vmo.get_size()?;
264 let mut buffer = vec![0u8; size as usize];
265 vmo.read(&mut buffer[..], 0)?;
266 Ok(buffer)
267 }
268
269 fn create_vmo(value: &str) -> zx::Vmo {
271 let vmo = zx::Vmo::create(value.len().try_into().unwrap()).unwrap();
272 vmo.write(value.as_bytes(), 0).unwrap();
273 vmo
274 }
275
276 fn duplicate_vmo(vmo: &Vmo) -> Vmo {
277 vmo.duplicate_handle(zx::Rights::BASIC | zx::Rights::READ | zx::Rights::MAP).ok().unwrap()
278 }
279
280 fn child_names(value: &str) -> Vec<String> {
281 match value {
282 SINGLE_CHILD_ROOT => vec![NAMED_CHILD_NODE.to_string()],
283 NAMED_CHILD_NODE => vec![NAMED_GRANDCHILD_NODE.to_string()],
284 MULTI_CHILD_ROOT => vec!["a".to_string(), "b".to_string(), "c".to_string()],
285 _ => vec![],
286 }
287 }
288}