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;
16use inspect_format::{BlockIndex, LinkNodeDisposition};
17use std::borrow::Cow;
18use std::sync::atomic::{AtomicBool, Ordering};
19
20#[derive(Debug, PartialEq, Eq, Default)]
27pub struct Node {
28 pub(crate) inner: Inner<InnerNodeType>,
29}
30
31impl InspectType for Node {}
32
33crate::impl_inspect_type_internal!(Node);
34
35impl Node {
36 pub fn clone_weak(&self) -> Node {
40 Self { inner: self.inner.clone_weak() }
41 }
42
43 #[must_use]
45 pub fn create_child<'a>(&self, name: impl Into<Cow<'a, str>>) -> Node {
46 self.inner
47 .inner_ref()
48 .and_then(|inner_ref| {
49 inner_ref
50 .state
51 .try_lock()
52 .and_then(|mut state| state.create_node(name.into(), inner_ref.block_index))
53 .map(|block_index| Node::new(inner_ref.state.clone(), block_index))
54 .ok()
55 })
56 .unwrap_or_else(Node::new_no_op)
57 }
58
59 pub fn record_child<'a, F>(&self, name: impl Into<Cow<'a, str>>, initialize: F)
61 where
62 F: FnOnce(&Node),
63 {
64 self.atomic_update(move |n| {
65 let child = n.create_child(name.into());
66 initialize(&child);
67 n.record(child);
68 });
69 }
70
71 pub fn atomic_update<F, R>(&self, update_fn: F) -> R
74 where
75 F: FnOnce(&Node) -> R,
76 {
77 self.atomic_access(update_fn)
78 }
79
80 pub fn record(&self, property: impl InspectType + 'static) {
82 if let Some(inner_ref) = self.inner.inner_ref() {
83 inner_ref.data.values.record(property);
84 }
85 }
86
87 pub fn clear_recorded(&self) {
89 if let Some(inner_ref) = self.inner.inner_ref() {
90 inner_ref.data.values.clear();
91 }
92 }
93
94 #[must_use]
96 pub fn create_int<'a>(&self, name: impl Into<Cow<'a, str>>, value: i64) -> IntProperty {
97 self.inner
98 .inner_ref()
99 .and_then(|inner_ref| {
100 inner_ref
101 .state
102 .try_lock()
103 .and_then(|mut state| {
104 state.create_int_metric(name.into(), value, inner_ref.block_index)
105 })
106 .map(|block_index| IntProperty::new(inner_ref.state.clone(), block_index))
107 .ok()
108 })
109 .unwrap_or_else(IntProperty::new_no_op)
110 }
111
112 pub fn record_int<'a>(&self, name: impl Into<Cow<'a, str>>, value: i64) {
114 let property = self.create_int(name.into(), value);
115 self.record(property);
116 }
117
118 #[must_use]
120 pub fn create_uint<'a>(&self, name: impl Into<Cow<'a, str>>, value: u64) -> UintProperty {
121 self.inner
122 .inner_ref()
123 .and_then(|inner_ref| {
124 inner_ref
125 .state
126 .try_lock()
127 .and_then(|mut state| {
128 state.create_uint_metric(name.into(), value, inner_ref.block_index)
129 })
130 .map(|block_index| UintProperty::new(inner_ref.state.clone(), block_index))
131 .ok()
132 })
133 .unwrap_or_else(UintProperty::new_no_op)
134 }
135
136 pub fn record_uint<'a>(&self, name: impl Into<Cow<'a, str>>, value: u64) {
138 let property = self.create_uint(name.into(), value);
139 self.record(property);
140 }
141
142 #[must_use]
144 pub fn create_double<'a>(&self, name: impl Into<Cow<'a, str>>, value: f64) -> DoubleProperty {
145 self.inner
146 .inner_ref()
147 .and_then(|inner_ref| {
148 inner_ref
149 .state
150 .try_lock()
151 .and_then(|mut state| {
152 state.create_double_metric(name.into(), value, inner_ref.block_index)
153 })
154 .map(|block_index| DoubleProperty::new(inner_ref.state.clone(), block_index))
155 .ok()
156 })
157 .unwrap_or_else(DoubleProperty::new_no_op)
158 }
159
160 pub fn record_double<'a>(&self, name: impl Into<Cow<'a, str>>, value: f64) {
162 let property = self.create_double(name.into(), value);
163 self.record(property);
164 }
165
166 #[must_use]
168 pub fn create_string_array<'a>(
169 &self,
170 name: impl Into<Cow<'a, str>>,
171 slots: usize,
172 ) -> StringArrayProperty {
173 self.inner
174 .inner_ref()
175 .and_then(|inner_ref| {
176 inner_ref
177 .state
178 .try_lock()
179 .and_then(|mut state| {
180 state.create_string_array(name.into(), slots, inner_ref.block_index)
181 })
182 .map(|block_index| {
183 StringArrayProperty::new(inner_ref.state.clone(), block_index)
184 })
185 .ok()
186 })
187 .unwrap_or_else(StringArrayProperty::new_no_op)
188 }
189
190 #[must_use]
192 pub fn create_int_array<'a>(
193 &self,
194 name: impl Into<Cow<'a, str>>,
195 slots: usize,
196 ) -> IntArrayProperty {
197 self.create_int_array_internal(name.into(), slots, ArrayFormat::Default)
198 }
199
200 #[must_use]
201 pub(crate) fn create_int_array_internal<'a>(
202 &self,
203 name: impl Into<Cow<'a, str>>,
204 slots: usize,
205 format: ArrayFormat,
206 ) -> IntArrayProperty {
207 self.inner
208 .inner_ref()
209 .and_then(|inner_ref| {
210 inner_ref
211 .state
212 .try_lock()
213 .and_then(|mut state| {
214 state.create_int_array(name.into(), slots, format, inner_ref.block_index)
215 })
216 .map(|block_index| IntArrayProperty::new(inner_ref.state.clone(), block_index))
217 .ok()
218 })
219 .unwrap_or_else(IntArrayProperty::new_no_op)
220 }
221
222 #[must_use]
224 pub fn create_uint_array<'a>(
225 &self,
226 name: impl Into<Cow<'a, str>>,
227 slots: usize,
228 ) -> UintArrayProperty {
229 self.create_uint_array_internal(name.into(), slots, ArrayFormat::Default)
230 }
231
232 #[must_use]
233 pub(crate) fn create_uint_array_internal<'a>(
234 &self,
235 name: impl Into<Cow<'a, str>>,
236 slots: usize,
237 format: ArrayFormat,
238 ) -> UintArrayProperty {
239 self.inner
240 .inner_ref()
241 .and_then(|inner_ref| {
242 inner_ref
243 .state
244 .try_lock()
245 .and_then(|mut state| {
246 state.create_uint_array(name.into(), slots, format, inner_ref.block_index)
247 })
248 .map(|block_index| UintArrayProperty::new(inner_ref.state.clone(), block_index))
249 .ok()
250 })
251 .unwrap_or_else(UintArrayProperty::new_no_op)
252 }
253
254 #[must_use]
256 pub fn create_double_array<'a>(
257 &self,
258 name: impl Into<Cow<'a, str>>,
259 slots: usize,
260 ) -> DoubleArrayProperty {
261 self.create_double_array_internal(name.into(), slots, ArrayFormat::Default)
262 }
263
264 #[must_use]
265 pub(crate) fn create_double_array_internal<'a>(
266 &self,
267 name: impl Into<Cow<'a, str>>,
268 slots: usize,
269 format: ArrayFormat,
270 ) -> DoubleArrayProperty {
271 self.inner
272 .inner_ref()
273 .and_then(|inner_ref| {
274 inner_ref
275 .state
276 .try_lock()
277 .and_then(|mut state| {
278 state.create_double_array(name.into(), slots, format, inner_ref.block_index)
279 })
280 .map(|block_index| {
281 DoubleArrayProperty::new(inner_ref.state.clone(), block_index)
282 })
283 .ok()
284 })
285 .unwrap_or_else(DoubleArrayProperty::new_no_op)
286 }
287
288 #[must_use]
290 pub fn create_int_linear_histogram<'a>(
291 &self,
292 name: impl Into<Cow<'a, str>>,
293 params: LinearHistogramParams<i64>,
294 ) -> IntLinearHistogramProperty {
295 IntLinearHistogramProperty::new(name.into(), params, self)
296 }
297
298 #[must_use]
300 pub fn create_uint_linear_histogram<'a>(
301 &self,
302 name: impl Into<Cow<'a, str>>,
303 params: LinearHistogramParams<u64>,
304 ) -> UintLinearHistogramProperty {
305 UintLinearHistogramProperty::new(name.into(), params, self)
306 }
307
308 #[must_use]
310 pub fn create_double_linear_histogram<'a>(
311 &self,
312 name: impl Into<Cow<'a, str>>,
313 params: LinearHistogramParams<f64>,
314 ) -> DoubleLinearHistogramProperty {
315 DoubleLinearHistogramProperty::new(name.into(), params, self)
316 }
317
318 #[must_use]
320 pub fn create_int_exponential_histogram<'a>(
321 &self,
322 name: impl Into<Cow<'a, str>>,
323 params: ExponentialHistogramParams<i64>,
324 ) -> IntExponentialHistogramProperty {
325 IntExponentialHistogramProperty::new(name.into(), params, self)
326 }
327
328 #[must_use]
330 pub fn create_uint_exponential_histogram<'a>(
331 &self,
332 name: impl Into<Cow<'a, str>>,
333 params: ExponentialHistogramParams<u64>,
334 ) -> UintExponentialHistogramProperty {
335 UintExponentialHistogramProperty::new(name.into(), params, self)
336 }
337
338 #[must_use]
340 pub fn create_double_exponential_histogram<'a>(
341 &self,
342 name: impl Into<Cow<'a, str>>,
343 params: ExponentialHistogramParams<f64>,
344 ) -> DoubleExponentialHistogramProperty {
345 DoubleExponentialHistogramProperty::new(name.into(), params, self)
346 }
347
348 #[must_use]
353 pub fn create_lazy_child<'a, F>(&self, name: impl Into<Cow<'a, str>>, callback: F) -> LazyNode
354 where
355 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
356 {
357 self.inner
358 .inner_ref()
359 .and_then(|inner_ref| {
360 inner_ref
361 .state
362 .try_lock()
363 .and_then(|mut state| {
364 state.create_lazy_node(
365 name.into(),
366 inner_ref.block_index,
367 LinkNodeDisposition::Child,
368 callback,
369 )
370 })
371 .map(|block_index| LazyNode::new(inner_ref.state.clone(), block_index))
372 .ok()
373 })
374 .unwrap_or_else(LazyNode::new_no_op)
375 }
376
377 pub fn record_lazy_child<'a, F>(&self, name: impl Into<Cow<'a, str>>, callback: F)
382 where
383 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
384 {
385 let property = self.create_lazy_child(name.into(), callback);
386 self.record(property);
387 }
388
389 #[must_use]
394 pub fn create_lazy_values<'a, F>(&self, name: impl Into<Cow<'a, str>>, callback: F) -> LazyNode
395 where
396 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
397 {
398 self.inner
399 .inner_ref()
400 .and_then(|inner_ref| {
401 inner_ref
402 .state
403 .try_lock()
404 .and_then(|mut state| {
405 state.create_lazy_node(
406 name.into(),
407 inner_ref.block_index,
408 LinkNodeDisposition::Inline,
409 callback,
410 )
411 })
412 .map(|block_index| LazyNode::new(inner_ref.state.clone(), block_index))
413 .ok()
414 })
415 .unwrap_or_else(LazyNode::new_no_op)
416 }
417
418 pub fn record_lazy_values<'a, F>(&self, name: impl Into<Cow<'a, str>>, callback: F)
423 where
424 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
425 {
426 let property = self.create_lazy_values(name.into(), callback);
427 self.record(property);
428 }
429
430 #[must_use]
432 pub fn create_string<'a, 'b>(
433 &self,
434 name: impl Into<Cow<'a, str>>,
435 value: impl Into<Cow<'b, str>>,
436 ) -> StringProperty {
437 self.inner
438 .inner_ref()
439 .and_then(|inner_ref| {
440 inner_ref
441 .state
442 .try_lock()
443 .and_then(|mut state| {
444 state.create_string(name.into(), value.into(), inner_ref.block_index)
445 })
446 .map(|block_index| StringProperty::new(inner_ref.state.clone(), block_index))
447 .ok()
448 })
449 .unwrap_or_else(StringProperty::new_no_op)
450 }
451
452 pub fn record_string<'a, 'b>(
454 &self,
455 name: impl Into<Cow<'a, str>>,
456 value: impl Into<Cow<'b, str>>,
457 ) {
458 let property = self.create_string(name, value);
459 self.record(property);
460 }
461
462 #[must_use]
464 pub fn create_bytes<'a>(
465 &self,
466 name: impl Into<Cow<'a, str>>,
467 value: impl AsRef<[u8]>,
468 ) -> BytesProperty {
469 self.inner
470 .inner_ref()
471 .and_then(|inner_ref| {
472 inner_ref
473 .state
474 .try_lock()
475 .and_then(|mut state| {
476 state.create_buffer_property(
477 name.into(),
478 value.as_ref(),
479 inner_ref.block_index,
480 )
481 })
482 .map(|block_index| BytesProperty::new(inner_ref.state.clone(), block_index))
483 .ok()
484 })
485 .unwrap_or_else(BytesProperty::new_no_op)
486 }
487
488 pub fn record_bytes<'a>(&self, name: impl Into<Cow<'a, str>>, value: impl AsRef<[u8]>) {
490 let property = self.create_bytes(name.into(), value);
491 self.record(property);
492 }
493
494 #[must_use]
496 pub fn create_bool<'a>(&self, name: impl Into<Cow<'a, str>>, value: bool) -> BoolProperty {
497 self.inner
498 .inner_ref()
499 .and_then(|inner_ref| {
500 inner_ref
501 .state
502 .try_lock()
503 .and_then(|mut state| {
504 state.create_bool(name.into(), value, inner_ref.block_index)
505 })
506 .map(|block_index| BoolProperty::new(inner_ref.state.clone(), block_index))
507 .ok()
508 })
509 .unwrap_or_else(BoolProperty::new_no_op)
510 }
511
512 pub fn record_bool<'a>(&self, name: impl Into<Cow<'a, str>>, value: bool) {
514 let property = self.create_bool(name.into(), value);
515 self.record(property);
516 }
517
518 pub fn adopt<T: InspectTypeReparentable>(&self, child: &T) -> Result<(), Error> {
520 child.reparent(self)
521 }
522
523 pub fn forget(&self) {
528 if let Some(inner_ref) = self.inner.inner_ref() {
529 let _ = InnerNodeType::free(&inner_ref.state, &inner_ref.data, inner_ref.block_index);
530 }
531 }
532
533 pub(crate) fn new_root(state: State) -> Node {
535 Node::new(state, BlockIndex::ROOT)
536 }
537}
538
539#[derive(Default, Debug)]
540pub(crate) struct InnerNodeType;
541
542#[derive(Default, Debug)]
543pub(crate) struct NodeData {
544 values: ValueList,
545 destroyed: AtomicBool,
546}
547
548impl InnerData for NodeData {
549 fn is_valid(&self) -> bool {
550 !self.destroyed.load(Ordering::SeqCst)
551 }
552}
553
554impl InnerType for InnerNodeType {
555 type Data = NodeData;
557
558 fn free(state: &State, data: &Self::Data, block_index: BlockIndex) -> Result<(), Error> {
559 if block_index == BlockIndex::ROOT {
560 return Ok(());
561 }
562 let mut state_lock = state.try_lock()?;
563 if data.destroyed.swap(true, Ordering::SeqCst) {
564 return Ok(());
565 }
566 state_lock.free_value(block_index).map_err(|err| Error::free("node", block_index, err))
567 }
568}
569
570#[cfg(test)]
571mod tests {
572 use super::*;
573 use crate::reader;
574 use crate::writer::testing_utils::{get_state, GetBlockExt};
575 use crate::writer::ArrayProperty;
576 use diagnostics_assertions::{assert_data_tree, assert_json_diff};
577 use futures::FutureExt;
578 use inspect_format::BlockType;
579
580 #[fuchsia::test]
581 fn node() {
582 let default = Node::default();
584 default.record_int("a", 0);
585
586 let state = get_state(4096);
587 let root = Node::new_root(state);
588 let node = root.create_child("node");
589 node.get_block::<_, inspect_format::Node>(|node_block| {
590 assert_eq!(node_block.block_type(), Some(BlockType::NodeValue));
591 assert_eq!(node_block.child_count(), 0);
592 });
593 {
594 let child = node.create_child("child");
595 child.get_block::<_, inspect_format::Node>(|child_block| {
596 assert_eq!(child_block.block_type(), Some(BlockType::NodeValue));
597 assert_eq!(child_block.child_count(), 0);
598 });
599 node.get_block::<_, inspect_format::Node>(|node_block| {
600 assert_eq!(node_block.child_count(), 1);
601 });
602 }
603 node.get_block::<_, inspect_format::Node>(|node_block| {
604 assert_eq!(node_block.child_count(), 0);
605 });
606 }
607
608 #[fuchsia::test]
609 async fn lazy_child() {
610 let inspector = Inspector::default();
611 let _lazy = inspector.root().create_lazy_child("lazy-1", || {
612 async move {
613 let insp = Inspector::default();
614 insp.root().record_lazy_child("parent", || {
615 async move {
616 let insp2 = Inspector::default();
617 insp2.root().record_int("create-lazy-child", 0);
618 insp2.root().record_int("create-lazy-child-2", 2);
619 Ok(insp2)
620 }
621 .boxed()
622 });
623 Ok(insp)
624 }
625 .boxed()
626 });
627
628 inspector.root().record_lazy_child("lazy-2", || {
629 async move {
630 let insp = Inspector::default();
631 insp.root().record_bool("recorded-lazy-child", true);
632 Ok(insp)
633 }
634 .boxed()
635 });
636
637 inspector.root().record_lazy_values("lazy", || {
638 async move {
639 let insp = Inspector::default();
640 insp.root().record_bool("recorded-lazy-values", true);
641 Ok(insp)
642 }
643 .boxed()
644 });
645
646 let result = reader::read(&inspector).await.unwrap();
647
648 assert_data_tree!(result, root: {
649 "lazy-1": {
650 "parent": {
651 "create-lazy-child": 0i64,
652 "create-lazy-child-2": 2i64,
653 },
654 },
655 "lazy-2": {
656 "recorded-lazy-child": true,
657 },
658 "recorded-lazy-values": true,
659 });
660 }
661
662 #[fuchsia::test]
663 fn test_adoption() {
664 let insp = Inspector::default();
665 let root = insp.root();
666 let a = root.create_child("a");
667 let b = root.create_child("b");
668 let c = b.create_child("c");
669
670 assert_data_tree!(insp, root: {
671 a: {},
672 b: {
673 c: {},
674 },
675 });
676
677 a.adopt(&b).unwrap();
678
679 assert_data_tree!(insp, root: {
680 a: {
681 b: {
682 c: {},
683 },
684 },
685 });
686
687 assert!(c.adopt(&a).is_err());
688 assert!(c.adopt(&b).is_err());
689 assert!(b.adopt(&a).is_err());
690 assert!(a.adopt(root).is_err());
691 assert!(a.adopt(&a).is_err());
692
693 {
694 let d = root.create_int("d", 4);
695
696 assert_data_tree!(insp, root: {
697 a: {
698 b: {
699 c: {},
700 },
701 },
702 d: 4i64,
703 });
704
705 c.adopt(&d).unwrap();
706
707 assert_data_tree!(insp, root: {
708 a: {
709 b: {
710 c: {
711 d: 4i64,
712 },
713 },
714 },
715 });
716 }
717
718 assert_data_tree!(insp, root: {
719 a: {
720 b: {
721 c: {},
722 },
723 },
724 });
725 }
726
727 #[fuchsia::test]
728 fn node_no_op_clone_weak() {
729 let default = Node::default();
730 assert!(!default.is_valid());
731 let weak = default.clone_weak();
732 assert!(!weak.is_valid());
733 let _ = weak.create_child("child");
734 std::mem::drop(default);
735 let _ = weak.create_uint("age", 1337);
736 assert!(!weak.is_valid());
737 }
738
739 #[fuchsia::test]
740 fn node_clone_weak() {
741 let state = get_state(4096);
742 let root = Node::new_root(state);
743 let node = root.create_child("node");
744 let node_weak = node.clone_weak();
745 let node_weak_2 = node_weak.clone_weak(); node.get_block::<_, inspect_format::Node>(|node_block| {
748 assert_eq!(node_block.block_type(), Some(BlockType::NodeValue));
749 assert_eq!(node_block.child_count(), 0);
750 });
751
752 let child_from_strong = node.create_child("child");
753 let child = node_weak.create_child("child_1");
754 let child_2 = node_weak_2.create_child("child_2");
755 std::mem::drop(node_weak_2);
756 node.get_block::<_, inspect_format::Node>(|block| {
757 assert_eq!(block.child_count(), 3);
758 });
759 std::mem::drop(child_from_strong);
760 node.get_block::<_, inspect_format::Node>(|block| {
761 assert_eq!(block.child_count(), 2);
762 });
763 std::mem::drop(child);
764 node.get_block::<_, inspect_format::Node>(|block| {
765 assert_eq!(block.child_count(), 1);
766 });
767 assert!(node_weak.is_valid());
768 assert!(child_2.is_valid());
769 std::mem::drop(node);
770 assert!(!node_weak.is_valid());
771 let _ = node_weak.create_child("orphan");
772 let _ = child_2.create_child("orphan");
773 }
774
775 #[fuchsia::test]
776 fn dummy_partialeq() {
777 let inspector = Inspector::default();
778 let root = inspector.root();
779
780 assert_eq!(root, &root.create_child("child1"));
784 assert_eq!(root.create_int("property1", 1), root.create_int("property2", 2));
785 assert_eq!(root.create_double("property1", 1.0), root.create_double("property2", 2.0));
786 assert_eq!(root.create_uint("property1", 1), root.create_uint("property2", 2));
787 assert_eq!(
788 root.create_string("property1", "value1"),
789 root.create_string("property2", "value2")
790 );
791 assert_eq!(
792 root.create_bytes("property1", b"value1"),
793 root.create_bytes("property2", b"value2")
794 );
795 }
796
797 #[fuchsia::test]
798 fn record() {
799 let inspector = Inspector::default();
800 let property = inspector.root().create_uint("a", 1);
801 inspector.root().record_uint("b", 2);
802 {
803 let child = inspector.root().create_child("child");
804 child.record(property);
805 child.record_double("c", 3.25);
806 assert_data_tree!(inspector, root: {
807 a: 1u64,
808 b: 2u64,
809 child: {
810 c: 3.25,
811 }
812 });
813 }
814 assert_data_tree!(inspector, root: {
817 b: 2u64,
818 });
819
820 inspector.root().clear_recorded();
821 assert_data_tree!(inspector, root: {});
822 }
823
824 #[fuchsia::test]
825 fn clear_recorded() {
826 let inspector = Inspector::default();
827 let one = inspector.root().create_child("one");
828 let two = inspector.root().create_child("two");
829 let one_recorded = one.create_child("one_recorded");
830 let two_recorded = two.create_child("two_recorded");
831
832 one.record(one_recorded);
833 two.record(two_recorded);
834
835 assert_json_diff!(inspector, root: {
836 one: {
837 one_recorded: {},
838 },
839 two: {
840 two_recorded: {},
841 },
842 });
843
844 two.clear_recorded();
845
846 assert_json_diff!(inspector, root: {
847 one: {
848 one_recorded: {},
849 },
850 two: {},
851 });
852
853 one.clear_recorded();
854
855 assert_json_diff!(inspector, root: {
856 one: {},
857 two: {},
858 });
859 }
860
861 #[fuchsia::test]
862 fn record_child() {
863 let inspector = Inspector::default();
864 inspector.root().record_child("test", |node| {
865 node.record_int("a", 1);
866 });
867 assert_data_tree!(inspector, root: {
868 test: {
869 a: 1i64,
870 }
871 })
872 }
873
874 #[fuchsia::test]
875 fn record_weak() {
876 let inspector = Inspector::default();
877 let main = inspector.root().create_child("main");
878 let main_weak = main.clone_weak();
879 let property = main_weak.create_uint("a", 1);
880
881 main_weak.record_uint("b", 2);
883 main.record_uint("c", 3);
884 {
885 let child = main_weak.create_child("child");
886 child.record(property);
887 child.record_double("c", 3.25);
888 assert_data_tree!(inspector, root: { main: {
889 a: 1u64,
890 b: 2u64,
891 c: 3u64,
892 child: {
893 c: 3.25,
894 }
895 }});
896 }
897 assert_data_tree!(inspector, root: { main: {
900 b: 2u64,
901 c: 3u64
902 }});
903 std::mem::drop(main);
904 main_weak.record_double("d", 1.0);
906 assert_data_tree!(inspector, root: { });
908 }
909
910 #[fuchsia::test]
911 fn string_arrays_on_record() {
912 let inspector = Inspector::default();
913 inspector.root().record_child("child", |node| {
914 node.record_int("my_int", 1i64);
915
916 let arr: crate::StringArrayProperty = node.create_string_array("my_string_array", 1);
917 arr.set(0, "test");
918 node.record(arr);
919 });
920 assert_data_tree!(inspector, root: {
921 child: {
922 my_int: 1i64,
923 my_string_array: vec!["test"]
924 }
925 });
926 }
927
928 #[fuchsia::test]
929 fn we_can_delete_a_node_explicitly_with_the_weak_clone() {
930 let insp = Inspector::default();
931 let a = insp.root().create_child("a");
932 let _property = a.create_int("foo", 1);
933 assert_data_tree!(insp, root: {
934 a: {
935 foo: 1i64,
936 }
937 });
938
939 let a_weak = a.clone_weak();
940 a_weak.forget();
941 assert!(a.inner.inner_ref().is_none());
942 assert_data_tree!(insp, root: {});
943 }
944}
945
946#[cfg(all(test, target_os = "fuchsia"))]
948mod fuchsia_tests {
949 use super::*;
950 use crate::hierarchy::DiagnosticsHierarchy;
951 use crate::{reader, NumericProperty};
952 use diagnostics_assertions::assert_json_diff;
953 use std::sync::Arc;
954 use zx::{self as zx, AsHandleRef, Peered};
955
956 #[fuchsia::test]
957 async fn atomic_update_reader() {
958 let inspector = Inspector::default();
959
960 let vmo = Arc::new(inspector.duplicate_vmo().expect("duplicate vmo handle"));
962 let (p1, p2) = zx::EventPair::create();
963
964 macro_rules! notify_and_wait_reader {
965 () => {
966 p1.signal_peer(zx::Signals::NONE, zx::Signals::USER_0).unwrap();
967 p1.wait_handle(zx::Signals::USER_0, zx::MonotonicInstant::INFINITE).unwrap();
968 p1.signal_handle(zx::Signals::USER_0, zx::Signals::NONE).unwrap();
969 };
970 }
971
972 macro_rules! wait_and_notify_writer {
973 ($code:block) => {
974 p2.wait_handle(zx::Signals::USER_0, zx::MonotonicInstant::INFINITE).unwrap();
975 p2.signal_handle(zx::Signals::USER_0, zx::Signals::NONE).unwrap();
976 $code
977 p2.signal_peer(zx::Signals::NONE, zx::Signals::USER_0).unwrap();
978 }
979 }
980
981 let thread = std::thread::spawn(move || {
982 wait_and_notify_writer! {{
984 let hierarchy: DiagnosticsHierarchy<String> =
985 reader::PartialNodeHierarchy::try_from(vmo.as_ref()).unwrap().into();
986 assert_eq!(hierarchy, DiagnosticsHierarchy::new_root());
987 }};
988 wait_and_notify_writer! {{
991 assert!(reader::PartialNodeHierarchy::try_from(vmo.as_ref()).is_err());
992 }};
993 wait_and_notify_writer! {{
996 assert!(reader::PartialNodeHierarchy::try_from(vmo.as_ref()).is_err());
997 }};
998 wait_and_notify_writer! {{
1001 assert!(reader::PartialNodeHierarchy::try_from(vmo.as_ref()).is_err());
1002 }};
1003 wait_and_notify_writer! {{
1005 let hierarchy: DiagnosticsHierarchy<String> =
1006 reader::PartialNodeHierarchy::try_from(vmo.as_ref()).unwrap().into();
1007 assert_json_diff!(hierarchy, root: {
1008 value: 2i64,
1009 child: {
1010 a: 1i64,
1011 b: 2i64,
1012 }
1013 });
1014 }};
1015 });
1016
1017 let mut child = Node::default();
1019 notify_and_wait_reader!();
1020 let int_val = inspector.root().create_int("value", 1);
1021 inspector
1022 .root()
1023 .atomic_update(|node| {
1024 child = node.create_child("child");
1026 notify_and_wait_reader!();
1027 child.record_int("a", 1);
1028 notify_and_wait_reader!();
1029 child.record_int("b", 2);
1030 notify_and_wait_reader!();
1031 int_val.add(1);
1032 Ok::<(), Error>(())
1033 })
1034 .expect("successful atomic update");
1035 notify_and_wait_reader!();
1036
1037 let _ = thread.join();
1039
1040 child.record_int("c", 3);
1042 assert_json_diff!(inspector, root: {
1043 value: 2i64,
1044 child: {
1045 a: 1i64,
1046 b: 2i64,
1047 c: 3i64,
1048 }
1049 });
1050 }
1051}