1use crate::writer::private::InspectTypeInternal;
6use crate::writer::{
7 BoolProperty, BytesProperty, DoubleArrayProperty, DoubleExponentialHistogramProperty,
8 DoubleLinearHistogramProperty, DoubleProperty, Error, Inner, InnerData, InnerType, InspectType,
9 InspectTypeReparentable, Inspector, IntArrayProperty, IntExponentialHistogramProperty,
10 IntLinearHistogramProperty, IntProperty, LazyNode, State, StringArrayProperty, StringProperty,
11 UintArrayProperty, UintExponentialHistogramProperty, UintLinearHistogramProperty, UintProperty,
12 ValueList,
13};
14use diagnostics_hierarchy::{ArrayFormat, ExponentialHistogramParams, LinearHistogramParams};
15use futures::future::{BoxFuture, LocalBoxFuture};
16use futures::task::{Context, Poll};
17use inspect_format::{BlockIndex, LinkNodeDisposition};
18use std::borrow::Cow;
19use std::pin::Pin;
20use std::sync::atomic::{AtomicBool, Ordering};
21
22struct SendFuture {
29 inner: fragile::Fragile<LocalBoxFuture<'static, Result<Inspector, anyhow::Error>>>,
30}
31
32impl futures::Future for SendFuture {
33 type Output = Result<Inspector, anyhow::Error>;
34
35 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
36 let mut pinned = std::pin::pin!(self.inner.get_mut());
37 pinned.as_mut().poll(cx)
38 }
39}
40
41#[derive(Debug, PartialEq, Eq, Default)]
48pub struct Node {
49 pub(crate) inner: Inner<InnerNodeType>,
50}
51
52impl InspectType for Node {
53 fn into_recorded(self) -> crate::writer::types::RecordedInspectType {
54 crate::writer::types::RecordedInspectType::Node(self)
55 }
56}
57
58crate::impl_inspect_type_internal!(Node);
59
60impl Node {
61 pub fn clone_weak(&self) -> Node {
65 Self { inner: self.inner.clone_weak() }
66 }
67
68 #[must_use]
70 pub fn create_child<'a>(&self, name: impl Into<Cow<'a, str>>) -> Node {
71 self.inner
72 .inner_ref()
73 .and_then(|inner_ref| {
74 inner_ref
75 .state
76 .try_lock()
77 .and_then(|mut state| state.create_node(name.into(), inner_ref.block_index))
78 .map(|block_index| Node::new(inner_ref.state.clone(), block_index))
79 .ok()
80 })
81 .unwrap_or_else(Node::new_no_op)
82 }
83
84 pub fn record_child<'a, F>(&self, name: impl Into<Cow<'a, str>>, initialize: F)
86 where
87 F: FnOnce(&Node),
88 {
89 self.atomic_update(move |n| {
90 let child = n.create_child(name.into());
91 initialize(&child);
92 n.record(child);
93 });
94 }
95
96 pub fn atomic_update<F, R>(&self, update_fn: F) -> R
99 where
100 F: FnOnce(&Node) -> R,
101 {
102 self.atomic_access(update_fn)
103 }
104
105 pub fn record(&self, property: impl InspectType + 'static) {
107 if let Some(inner_ref) = self.inner.inner_ref() {
108 inner_ref.data.values.record(property);
109 }
110 }
111
112 pub fn clear_recorded(&self) {
114 if let Some(inner_ref) = self.inner.inner_ref() {
115 inner_ref.data.values.clear();
116 }
117 }
118
119 #[must_use]
121 pub fn create_int<'a>(&self, name: impl Into<Cow<'a, str>>, value: i64) -> IntProperty {
122 self.inner
123 .inner_ref()
124 .and_then(|inner_ref| {
125 inner_ref
126 .state
127 .try_lock()
128 .and_then(|mut state| {
129 state.create_int_metric(name.into(), value, inner_ref.block_index)
130 })
131 .map(|block_index| IntProperty::new(inner_ref.state.clone(), block_index))
132 .ok()
133 })
134 .unwrap_or_else(IntProperty::new_no_op)
135 }
136
137 pub fn record_int<'a>(&self, name: impl Into<Cow<'a, str>>, value: i64) {
139 let property = self.create_int(name.into(), value);
140 self.record(property);
141 }
142
143 #[must_use]
145 pub fn create_uint<'a>(&self, name: impl Into<Cow<'a, str>>, value: u64) -> UintProperty {
146 self.inner
147 .inner_ref()
148 .and_then(|inner_ref| {
149 inner_ref
150 .state
151 .try_lock()
152 .and_then(|mut state| {
153 state.create_uint_metric(name.into(), value, inner_ref.block_index)
154 })
155 .map(|block_index| UintProperty::new(inner_ref.state.clone(), block_index))
156 .ok()
157 })
158 .unwrap_or_else(UintProperty::new_no_op)
159 }
160
161 pub fn record_uint<'a>(&self, name: impl Into<Cow<'a, str>>, value: u64) {
163 let property = self.create_uint(name.into(), value);
164 self.record(property);
165 }
166
167 #[must_use]
169 pub fn create_double<'a>(&self, name: impl Into<Cow<'a, str>>, value: f64) -> DoubleProperty {
170 self.inner
171 .inner_ref()
172 .and_then(|inner_ref| {
173 inner_ref
174 .state
175 .try_lock()
176 .and_then(|mut state| {
177 state.create_double_metric(name.into(), value, inner_ref.block_index)
178 })
179 .map(|block_index| DoubleProperty::new(inner_ref.state.clone(), block_index))
180 .ok()
181 })
182 .unwrap_or_else(DoubleProperty::new_no_op)
183 }
184
185 pub fn record_double<'a>(&self, name: impl Into<Cow<'a, str>>, value: f64) {
187 let property = self.create_double(name.into(), value);
188 self.record(property);
189 }
190
191 #[must_use]
193 pub fn create_string_array<'a>(
194 &self,
195 name: impl Into<Cow<'a, str>>,
196 slots: usize,
197 ) -> StringArrayProperty {
198 self.inner
199 .inner_ref()
200 .and_then(|inner_ref| {
201 inner_ref
202 .state
203 .try_lock()
204 .and_then(|mut state| {
205 state.create_string_array(name.into(), slots, inner_ref.block_index)
206 })
207 .map(|block_index| {
208 StringArrayProperty::new(inner_ref.state.clone(), block_index)
209 })
210 .ok()
211 })
212 .unwrap_or_else(StringArrayProperty::new_no_op)
213 }
214
215 #[must_use]
217 pub fn create_int_array<'a>(
218 &self,
219 name: impl Into<Cow<'a, str>>,
220 slots: usize,
221 ) -> IntArrayProperty {
222 self.create_int_array_internal(name.into(), slots, ArrayFormat::Default)
223 }
224
225 #[must_use]
226 pub(crate) fn create_int_array_internal<'a>(
227 &self,
228 name: impl Into<Cow<'a, str>>,
229 slots: usize,
230 format: ArrayFormat,
231 ) -> IntArrayProperty {
232 self.inner
233 .inner_ref()
234 .and_then(|inner_ref| {
235 inner_ref
236 .state
237 .try_lock()
238 .and_then(|mut state| {
239 state.create_int_array(name.into(), slots, format, inner_ref.block_index)
240 })
241 .map(|block_index| IntArrayProperty::new(inner_ref.state.clone(), block_index))
242 .ok()
243 })
244 .unwrap_or_else(IntArrayProperty::new_no_op)
245 }
246
247 #[must_use]
249 pub fn create_uint_array<'a>(
250 &self,
251 name: impl Into<Cow<'a, str>>,
252 slots: usize,
253 ) -> UintArrayProperty {
254 self.create_uint_array_internal(name.into(), slots, ArrayFormat::Default)
255 }
256
257 #[must_use]
258 pub(crate) fn create_uint_array_internal<'a>(
259 &self,
260 name: impl Into<Cow<'a, str>>,
261 slots: usize,
262 format: ArrayFormat,
263 ) -> UintArrayProperty {
264 self.inner
265 .inner_ref()
266 .and_then(|inner_ref| {
267 inner_ref
268 .state
269 .try_lock()
270 .and_then(|mut state| {
271 state.create_uint_array(name.into(), slots, format, inner_ref.block_index)
272 })
273 .map(|block_index| UintArrayProperty::new(inner_ref.state.clone(), block_index))
274 .ok()
275 })
276 .unwrap_or_else(UintArrayProperty::new_no_op)
277 }
278
279 #[must_use]
281 pub fn create_double_array<'a>(
282 &self,
283 name: impl Into<Cow<'a, str>>,
284 slots: usize,
285 ) -> DoubleArrayProperty {
286 self.create_double_array_internal(name.into(), slots, ArrayFormat::Default)
287 }
288
289 #[must_use]
290 pub(crate) fn create_double_array_internal<'a>(
291 &self,
292 name: impl Into<Cow<'a, str>>,
293 slots: usize,
294 format: ArrayFormat,
295 ) -> DoubleArrayProperty {
296 self.inner
297 .inner_ref()
298 .and_then(|inner_ref| {
299 inner_ref
300 .state
301 .try_lock()
302 .and_then(|mut state| {
303 state.create_double_array(name.into(), slots, format, inner_ref.block_index)
304 })
305 .map(|block_index| {
306 DoubleArrayProperty::new(inner_ref.state.clone(), block_index)
307 })
308 .ok()
309 })
310 .unwrap_or_else(DoubleArrayProperty::new_no_op)
311 }
312
313 #[must_use]
315 pub fn create_int_linear_histogram<'a>(
316 &self,
317 name: impl Into<Cow<'a, str>>,
318 params: LinearHistogramParams<i64>,
319 ) -> IntLinearHistogramProperty {
320 IntLinearHistogramProperty::new(name.into(), params, self)
321 }
322
323 #[must_use]
325 pub fn create_uint_linear_histogram<'a>(
326 &self,
327 name: impl Into<Cow<'a, str>>,
328 params: LinearHistogramParams<u64>,
329 ) -> UintLinearHistogramProperty {
330 UintLinearHistogramProperty::new(name.into(), params, self)
331 }
332
333 #[must_use]
335 pub fn create_double_linear_histogram<'a>(
336 &self,
337 name: impl Into<Cow<'a, str>>,
338 params: LinearHistogramParams<f64>,
339 ) -> DoubleLinearHistogramProperty {
340 DoubleLinearHistogramProperty::new(name.into(), params, self)
341 }
342
343 #[must_use]
345 pub fn create_int_exponential_histogram<'a>(
346 &self,
347 name: impl Into<Cow<'a, str>>,
348 params: ExponentialHistogramParams<i64>,
349 ) -> IntExponentialHistogramProperty {
350 IntExponentialHistogramProperty::new(name.into(), params, self)
351 }
352
353 #[must_use]
355 pub fn create_uint_exponential_histogram<'a>(
356 &self,
357 name: impl Into<Cow<'a, str>>,
358 params: ExponentialHistogramParams<u64>,
359 ) -> UintExponentialHistogramProperty {
360 UintExponentialHistogramProperty::new(name.into(), params, self)
361 }
362
363 #[must_use]
365 pub fn create_double_exponential_histogram<'a>(
366 &self,
367 name: impl Into<Cow<'a, str>>,
368 params: ExponentialHistogramParams<f64>,
369 ) -> DoubleExponentialHistogramProperty {
370 DoubleExponentialHistogramProperty::new(name.into(), params, self)
371 }
372
373 #[must_use]
378 pub fn create_lazy_child<'a, F>(&self, name: impl Into<Cow<'a, str>>, callback: F) -> LazyNode
379 where
380 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
381 {
382 self.inner
383 .inner_ref()
384 .and_then(|inner_ref| {
385 inner_ref
386 .state
387 .try_lock()
388 .and_then(|mut state| {
389 state.create_lazy_node(
390 name.into(),
391 inner_ref.block_index,
392 LinkNodeDisposition::Child,
393 callback,
394 )
395 })
396 .map(|block_index| LazyNode::new(inner_ref.state.clone(), block_index))
397 .ok()
398 })
399 .unwrap_or_else(LazyNode::new_no_op)
400 }
401
402 pub fn record_lazy_child<'a, F>(&self, name: impl Into<Cow<'a, str>>, callback: F)
407 where
408 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
409 {
410 let property = self.create_lazy_child(name.into(), callback);
411 self.record(property);
412 }
413
414 #[must_use]
422 pub fn create_lazy_child_with_thread_local<'a, F>(
423 &self,
424 name: impl Into<Cow<'a, str>>,
425 callback: F,
426 ) -> LazyNode
427 where
428 F: Fn() -> LocalBoxFuture<'static, Result<Inspector, anyhow::Error>> + 'static,
429 {
430 let fragile_callback = fragile::Fragile::new(callback);
431 self.create_lazy_child(name, move || {
432 let cb = fragile_callback.get();
433 Box::pin(SendFuture { inner: fragile::Fragile::new(cb()) })
434 })
435 }
436
437 pub fn record_lazy_child_with_thread_local<'a, F>(
445 &self,
446 name: impl Into<Cow<'a, str>>,
447 callback: F,
448 ) where
449 F: Fn() -> LocalBoxFuture<'static, Result<Inspector, anyhow::Error>> + 'static,
450 {
451 let property = self.create_lazy_child_with_thread_local(name.into(), callback);
452 self.record(property);
453 }
454
455 #[must_use]
460 pub fn create_lazy_values<'a, F>(&self, name: impl Into<Cow<'a, str>>, callback: F) -> LazyNode
461 where
462 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
463 {
464 self.inner
465 .inner_ref()
466 .and_then(|inner_ref| {
467 inner_ref
468 .state
469 .try_lock()
470 .and_then(|mut state| {
471 state.create_lazy_node(
472 name.into(),
473 inner_ref.block_index,
474 LinkNodeDisposition::Inline,
475 callback,
476 )
477 })
478 .map(|block_index| LazyNode::new(inner_ref.state.clone(), block_index))
479 .ok()
480 })
481 .unwrap_or_else(LazyNode::new_no_op)
482 }
483
484 pub fn record_lazy_values<'a, F>(&self, name: impl Into<Cow<'a, str>>, callback: F)
489 where
490 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
491 {
492 let property = self.create_lazy_values(name.into(), callback);
493 self.record(property);
494 }
495
496 #[must_use]
504 pub fn create_lazy_values_with_thread_local<'a, F>(
505 &self,
506 name: impl Into<Cow<'a, str>>,
507 callback: F,
508 ) -> LazyNode
509 where
510 F: Fn() -> LocalBoxFuture<'static, Result<Inspector, anyhow::Error>> + 'static,
511 {
512 let fragile_callback = fragile::Fragile::new(callback);
513 self.create_lazy_values(name, move || {
514 let cb = fragile_callback.get();
515 Box::pin(SendFuture { inner: fragile::Fragile::new(cb()) })
516 })
517 }
518
519 pub fn record_lazy_values_with_thread_local<'a, F>(
527 &self,
528 name: impl Into<Cow<'a, str>>,
529 callback: F,
530 ) where
531 F: Fn() -> LocalBoxFuture<'static, Result<Inspector, anyhow::Error>> + 'static,
532 {
533 let property = self.create_lazy_values_with_thread_local(name.into(), callback);
534 self.record(property);
535 }
536
537 #[must_use]
539 pub fn create_string<'a, 'b>(
540 &self,
541 name: impl Into<Cow<'a, str>>,
542 value: impl Into<Cow<'b, str>>,
543 ) -> StringProperty {
544 self.inner
545 .inner_ref()
546 .and_then(|inner_ref| {
547 inner_ref
548 .state
549 .try_lock()
550 .and_then(|mut state| {
551 state.create_string(name.into(), value.into(), inner_ref.block_index)
552 })
553 .map(|block_index| StringProperty::new(inner_ref.state.clone(), block_index))
554 .ok()
555 })
556 .unwrap_or_else(StringProperty::new_no_op)
557 }
558
559 pub fn record_string<'a, 'b>(
561 &self,
562 name: impl Into<Cow<'a, str>>,
563 value: impl Into<Cow<'b, str>>,
564 ) {
565 let property = self.create_string(name, value);
566 self.record(property);
567 }
568
569 #[must_use]
571 pub fn create_bytes<'a>(
572 &self,
573 name: impl Into<Cow<'a, str>>,
574 value: impl AsRef<[u8]>,
575 ) -> BytesProperty {
576 self.inner
577 .inner_ref()
578 .and_then(|inner_ref| {
579 inner_ref
580 .state
581 .try_lock()
582 .and_then(|mut state| {
583 state.create_buffer_property(
584 name.into(),
585 value.as_ref(),
586 inner_ref.block_index,
587 )
588 })
589 .map(|block_index| BytesProperty::new(inner_ref.state.clone(), block_index))
590 .ok()
591 })
592 .unwrap_or_else(BytesProperty::new_no_op)
593 }
594
595 pub fn record_bytes<'a>(&self, name: impl Into<Cow<'a, str>>, value: impl AsRef<[u8]>) {
597 let property = self.create_bytes(name.into(), value);
598 self.record(property);
599 }
600
601 #[must_use]
603 pub fn create_bool<'a>(&self, name: impl Into<Cow<'a, str>>, value: bool) -> BoolProperty {
604 self.inner
605 .inner_ref()
606 .and_then(|inner_ref| {
607 inner_ref
608 .state
609 .try_lock()
610 .and_then(|mut state| {
611 state.create_bool(name.into(), value, inner_ref.block_index)
612 })
613 .map(|block_index| BoolProperty::new(inner_ref.state.clone(), block_index))
614 .ok()
615 })
616 .unwrap_or_else(BoolProperty::new_no_op)
617 }
618
619 pub fn record_bool<'a>(&self, name: impl Into<Cow<'a, str>>, value: bool) {
621 let property = self.create_bool(name.into(), value);
622 self.record(property);
623 }
624
625 pub fn adopt<T: InspectTypeReparentable>(&self, child: &T) -> Result<(), Error> {
627 child.reparent(self)
628 }
629
630 pub fn forget(&self) {
635 if let Some(inner_ref) = self.inner.inner_ref() {
636 let _ = InnerNodeType::free(&inner_ref.state, &inner_ref.data, inner_ref.block_index);
637 }
638 }
639
640 pub(crate) fn new_root(state: State) -> Node {
642 Node::new(state, BlockIndex::ROOT)
643 }
644}
645
646#[derive(Default, Debug)]
647pub(crate) struct InnerNodeType;
648
649#[derive(Default, Debug)]
650pub(crate) struct NodeData {
651 values: ValueList,
652 destroyed: AtomicBool,
653}
654
655impl InnerData for NodeData {
656 fn is_valid(&self) -> bool {
657 !self.destroyed.load(Ordering::SeqCst)
658 }
659}
660
661impl InnerType for InnerNodeType {
662 type Data = NodeData;
664
665 fn free(state: &State, data: &Self::Data, block_index: BlockIndex) -> Result<(), Error> {
666 if block_index == BlockIndex::ROOT {
667 return Ok(());
668 }
669 let mut state_lock = state.try_lock()?;
670 if data.destroyed.swap(true, Ordering::SeqCst) {
671 return Ok(());
672 }
673 state_lock.free_value(block_index).map_err(|err| Error::free("node", block_index, err))
674 }
675}
676
677#[cfg(test)]
678mod tests {
679 use super::*;
680 use crate::reader;
681 use crate::writer::ArrayProperty;
682 use crate::writer::testing_utils::{GetBlockExt, get_state};
683 use diagnostics_assertions::{assert_data_tree, assert_json_diff};
684 use futures::FutureExt;
685 use inspect_format::BlockType;
686
687 #[fuchsia::test]
688 fn node() {
689 let default = Node::default();
691 default.record_int("a", 0);
692
693 let state = get_state(4096);
694 let root = Node::new_root(state);
695 let node = root.create_child("node");
696 node.get_block::<_, inspect_format::Node>(|node_block| {
697 assert_eq!(node_block.block_type(), Some(BlockType::NodeValue));
698 assert_eq!(node_block.child_count(), 0);
699 });
700 {
701 let child = node.create_child("child");
702 child.get_block::<_, inspect_format::Node>(|child_block| {
703 assert_eq!(child_block.block_type(), Some(BlockType::NodeValue));
704 assert_eq!(child_block.child_count(), 0);
705 });
706 node.get_block::<_, inspect_format::Node>(|node_block| {
707 assert_eq!(node_block.child_count(), 1);
708 });
709 }
710 node.get_block::<_, inspect_format::Node>(|node_block| {
711 assert_eq!(node_block.child_count(), 0);
712 });
713 }
714
715 #[fuchsia::test]
716 async fn lazy_child() {
717 let inspector = Inspector::default();
718 let _lazy = inspector.root().create_lazy_child("lazy-1", || {
719 async move {
720 let insp = Inspector::default();
721 insp.root().record_lazy_child("parent", || {
722 async move {
723 let insp2 = Inspector::default();
724 insp2.root().record_int("create-lazy-child", 0);
725 insp2.root().record_int("create-lazy-child-2", 2);
726 Ok(insp2)
727 }
728 .boxed()
729 });
730 Ok(insp)
731 }
732 .boxed()
733 });
734
735 inspector.root().record_lazy_child("lazy-2", || {
736 async move {
737 let insp = Inspector::default();
738 insp.root().record_bool("recorded-lazy-child", true);
739 Ok(insp)
740 }
741 .boxed()
742 });
743
744 inspector.root().record_lazy_values("lazy", || {
745 async move {
746 let insp = Inspector::default();
747 insp.root().record_bool("recorded-lazy-values", true);
748 Ok(insp)
749 }
750 .boxed()
751 });
752
753 let result = reader::read(&inspector).await.unwrap();
754
755 assert_data_tree!(result, root: {
756 "lazy-1": {
757 "parent": {
758 "create-lazy-child": 0i64,
759 "create-lazy-child-2": 2i64,
760 },
761 },
762 "lazy-2": {
763 "recorded-lazy-child": true,
764 },
765 "recorded-lazy-values": true,
766 });
767 }
768
769 #[fuchsia::test]
770 async fn lazy_child_with_thread_local() {
771 let inspector = Inspector::default();
772 inspector.root().record_lazy_child_with_thread_local("lazy-1", || {
773 async move {
774 let insp = Inspector::default();
775 insp.root().record_int("a", 1i64);
776 Ok(insp)
777 }
778 .boxed()
779 });
780
781 let result = reader::read(&inspector).await.unwrap();
782 assert_data_tree!(result, root: {
783 "lazy-1": {
784 "a": 1i64,
785 },
786 });
787 }
788
789 #[fuchsia::test]
790 async fn lazy_values_with_thread_local() {
791 let inspector = Inspector::default();
792 inspector.root().record_lazy_values_with_thread_local("lazy-1", || {
793 async move {
794 let insp = Inspector::default();
795 insp.root().record_int("a", 1i64);
796 Ok(insp)
797 }
798 .boxed()
799 });
800
801 let result = reader::read(&inspector).await.unwrap();
802 assert_data_tree!(result, root: {
803 "a": 1i64,
804 });
805 }
806
807 #[fuchsia::test]
808 async fn test_adoption() {
809 let insp = Inspector::default();
810 let root = insp.root();
811 let a = root.create_child("a");
812 let b = root.create_child("b");
813 let c = b.create_child("c");
814
815 assert_data_tree!(insp, root: {
816 a: {},
817 b: {
818 c: {},
819 },
820 });
821
822 a.adopt(&b).unwrap();
823
824 assert_data_tree!(insp, root: {
825 a: {
826 b: {
827 c: {},
828 },
829 },
830 });
831
832 assert!(c.adopt(&a).is_err());
833 assert!(c.adopt(&b).is_err());
834 assert!(b.adopt(&a).is_err());
835 assert!(a.adopt(root).is_err());
836 assert!(a.adopt(&a).is_err());
837
838 {
839 let d = root.create_int("d", 4);
840
841 assert_data_tree!(insp, root: {
842 a: {
843 b: {
844 c: {},
845 },
846 },
847 d: 4i64,
848 });
849
850 c.adopt(&d).unwrap();
851
852 assert_data_tree!(insp, root: {
853 a: {
854 b: {
855 c: {
856 d: 4i64,
857 },
858 },
859 },
860 });
861 }
862
863 assert_data_tree!(insp, root: {
864 a: {
865 b: {
866 c: {},
867 },
868 },
869 });
870 }
871
872 #[fuchsia::test]
873 fn node_no_op_clone_weak() {
874 let default = Node::default();
875 assert!(!default.is_valid());
876 let weak = default.clone_weak();
877 assert!(!weak.is_valid());
878 let _ = weak.create_child("child");
879 std::mem::drop(default);
880 let _ = weak.create_uint("age", 1337);
881 assert!(!weak.is_valid());
882 }
883
884 #[fuchsia::test]
885 fn node_clone_weak() {
886 let state = get_state(4096);
887 let root = Node::new_root(state);
888 let node = root.create_child("node");
889 let node_weak = node.clone_weak();
890 let node_weak_2 = node_weak.clone_weak(); node.get_block::<_, inspect_format::Node>(|node_block| {
893 assert_eq!(node_block.block_type(), Some(BlockType::NodeValue));
894 assert_eq!(node_block.child_count(), 0);
895 });
896
897 let child_from_strong = node.create_child("child");
898 let child = node_weak.create_child("child_1");
899 let child_2 = node_weak_2.create_child("child_2");
900 std::mem::drop(node_weak_2);
901 node.get_block::<_, inspect_format::Node>(|block| {
902 assert_eq!(block.child_count(), 3);
903 });
904 std::mem::drop(child_from_strong);
905 node.get_block::<_, inspect_format::Node>(|block| {
906 assert_eq!(block.child_count(), 2);
907 });
908 std::mem::drop(child);
909 node.get_block::<_, inspect_format::Node>(|block| {
910 assert_eq!(block.child_count(), 1);
911 });
912 assert!(node_weak.is_valid());
913 assert!(child_2.is_valid());
914 std::mem::drop(node);
915 assert!(!node_weak.is_valid());
916 let _ = node_weak.create_child("orphan");
917 let _ = child_2.create_child("orphan");
918 }
919
920 #[fuchsia::test]
921 fn dummy_partialeq() {
922 let inspector = Inspector::default();
923 let root = inspector.root();
924
925 assert_eq!(root, &root.create_child("child1"));
929 assert_eq!(root.create_int("property1", 1), root.create_int("property2", 2));
930 assert_eq!(root.create_double("property1", 1.0), root.create_double("property2", 2.0));
931 assert_eq!(root.create_uint("property1", 1), root.create_uint("property2", 2));
932 assert_eq!(
933 root.create_string("property1", "value1"),
934 root.create_string("property2", "value2")
935 );
936 assert_eq!(
937 root.create_bytes("property1", b"value1"),
938 root.create_bytes("property2", b"value2")
939 );
940 }
941
942 #[fuchsia::test]
943 async fn record() {
944 let inspector = Inspector::default();
945 let property = inspector.root().create_uint("a", 1);
946 inspector.root().record_uint("b", 2);
947 {
948 let child = inspector.root().create_child("child");
949 child.record(property);
950 child.record_double("c", 3.25);
951 assert_data_tree!(inspector, root: {
952 a: 1u64,
953 b: 2u64,
954 child: {
955 c: 3.25,
956 }
957 });
958 }
959 assert_data_tree!(inspector, root: {
962 b: 2u64,
963 });
964
965 inspector.root().clear_recorded();
966 assert_data_tree!(inspector, root: {});
967 }
968
969 #[fuchsia::test]
970 async fn clear_recorded() {
971 let inspector = Inspector::default();
972 let one = inspector.root().create_child("one");
973 let two = inspector.root().create_child("two");
974 let one_recorded = one.create_child("one_recorded");
975 let two_recorded = two.create_child("two_recorded");
976
977 one.record(one_recorded);
978 two.record(two_recorded);
979
980 assert_json_diff!(inspector, root: {
981 one: {
982 one_recorded: {},
983 },
984 two: {
985 two_recorded: {},
986 },
987 });
988
989 two.clear_recorded();
990
991 assert_json_diff!(inspector, root: {
992 one: {
993 one_recorded: {},
994 },
995 two: {},
996 });
997
998 one.clear_recorded();
999
1000 assert_json_diff!(inspector, root: {
1001 one: {},
1002 two: {},
1003 });
1004 }
1005
1006 #[fuchsia::test]
1007 async fn record_child() {
1008 let inspector = Inspector::default();
1009 inspector.root().record_child("test", |node| {
1010 node.record_int("a", 1);
1011 });
1012 assert_data_tree!(inspector, root: {
1013 test: {
1014 a: 1i64,
1015 }
1016 })
1017 }
1018
1019 #[fuchsia::test]
1020 async fn record_weak() {
1021 let inspector = Inspector::default();
1022 let main = inspector.root().create_child("main");
1023 let main_weak = main.clone_weak();
1024 let property = main_weak.create_uint("a", 1);
1025
1026 main_weak.record_uint("b", 2);
1028 main.record_uint("c", 3);
1029 {
1030 let child = main_weak.create_child("child");
1031 child.record(property);
1032 child.record_double("c", 3.25);
1033 assert_data_tree!(inspector, root: { main: {
1034 a: 1u64,
1035 b: 2u64,
1036 c: 3u64,
1037 child: {
1038 c: 3.25,
1039 }
1040 }});
1041 }
1042 assert_data_tree!(inspector, root: { main: {
1045 b: 2u64,
1046 c: 3u64
1047 }});
1048 std::mem::drop(main);
1049 main_weak.record_double("d", 1.0);
1051 assert_data_tree!(inspector, root: { });
1053 }
1054
1055 #[fuchsia::test]
1056 async fn string_arrays_on_record() {
1057 let inspector = Inspector::default();
1058 inspector.root().record_child("child", |node| {
1059 node.record_int("my_int", 1i64);
1060
1061 let arr: crate::StringArrayProperty = node.create_string_array("my_string_array", 1);
1062 arr.set(0, "test");
1063 node.record(arr);
1064 });
1065 assert_data_tree!(inspector, root: {
1066 child: {
1067 my_int: 1i64,
1068 my_string_array: vec!["test"]
1069 }
1070 });
1071 }
1072
1073 #[fuchsia::test]
1074 async fn we_can_delete_a_node_explicitly_with_the_weak_clone() {
1075 let insp = Inspector::default();
1076 let a = insp.root().create_child("a");
1077 let _property = a.create_int("foo", 1);
1078 assert_data_tree!(insp, root: {
1079 a: {
1080 foo: 1i64,
1081 }
1082 });
1083
1084 let a_weak = a.clone_weak();
1085 a_weak.forget();
1086 assert!(a.inner.inner_ref().is_none());
1087 assert_data_tree!(insp, root: {});
1088 }
1089}
1090
1091#[cfg(all(test, target_os = "fuchsia"))]
1093mod fuchsia_tests {
1094 use super::*;
1095 use crate::hierarchy::DiagnosticsHierarchy;
1096 use crate::{NumericProperty, reader};
1097 use diagnostics_assertions::assert_json_diff;
1098 use std::sync::Arc;
1099 use zx::Peered;
1100
1101 #[fuchsia::test]
1102 fn atomic_update_reader() {
1103 let inspector = Inspector::default();
1104
1105 let vmo = Arc::new(inspector.duplicate_vmo().expect("duplicate vmo handle"));
1107 let (p1, p2) = zx::EventPair::create();
1108
1109 macro_rules! notify_and_wait_reader {
1110 () => {
1111 p1.signal_peer(zx::Signals::NONE, zx::Signals::USER_0).unwrap();
1112 p1.wait_one(zx::Signals::USER_0, zx::MonotonicInstant::INFINITE).unwrap();
1113 p1.signal(zx::Signals::USER_0, zx::Signals::NONE).unwrap();
1114 };
1115 }
1116
1117 macro_rules! wait_and_notify_writer {
1118 ($code:block) => {
1119 p2.wait_one(zx::Signals::USER_0, zx::MonotonicInstant::INFINITE).unwrap();
1120 p2.signal(zx::Signals::USER_0, zx::Signals::NONE).unwrap();
1121 $code
1122 p2.signal_peer(zx::Signals::NONE, zx::Signals::USER_0).unwrap();
1123 }
1124 }
1125
1126 let thread = std::thread::spawn(move || {
1127 wait_and_notify_writer! {{
1129 let hierarchy: DiagnosticsHierarchy<String> =
1130 reader::PartialNodeHierarchy::try_from(vmo.as_ref()).unwrap().into();
1131 assert_eq!(hierarchy, DiagnosticsHierarchy::new_root());
1132 }};
1133 wait_and_notify_writer! {{
1136 assert!(reader::PartialNodeHierarchy::try_from(vmo.as_ref()).is_err());
1137 }};
1138 wait_and_notify_writer! {{
1141 assert!(reader::PartialNodeHierarchy::try_from(vmo.as_ref()).is_err());
1142 }};
1143 wait_and_notify_writer! {{
1146 assert!(reader::PartialNodeHierarchy::try_from(vmo.as_ref()).is_err());
1147 }};
1148 wait_and_notify_writer! {{
1150 let hierarchy: DiagnosticsHierarchy<String> =
1151 reader::PartialNodeHierarchy::try_from(vmo.as_ref()).unwrap().into();
1152 fuchsia_async::TestExecutor::new().run_singlethreaded(async move {
1155 assert_json_diff!(hierarchy, root: {
1156 value: 2i64,
1157 child: {
1158 a: 1i64,
1159 b: 2i64,
1160 }
1161 });
1162 });
1163 }};
1164 });
1165
1166 let mut child = Node::default();
1168 notify_and_wait_reader!();
1169 let int_val = inspector.root().create_int("value", 1);
1170 inspector
1171 .root()
1172 .atomic_update(|node| {
1173 child = node.create_child("child");
1175 notify_and_wait_reader!();
1176 child.record_int("a", 1);
1177 notify_and_wait_reader!();
1178 child.record_int("b", 2);
1179 notify_and_wait_reader!();
1180 int_val.add(1);
1181 Ok::<(), Error>(())
1182 })
1183 .expect("successful atomic update");
1184 notify_and_wait_reader!();
1185
1186 let _ = thread.join();
1188
1189 child.record_int("c", 3);
1191 fuchsia_async::TestExecutor::new().run_singlethreaded(async move {
1192 assert_json_diff!(inspector, root: {
1193 value: 2i64,
1194 child: {
1195 a: 1i64,
1196 b: 2i64,
1197 c: 3i64,
1198 }
1199 });
1200 });
1201 }
1202}