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