1use anyhow::{Error, bail, format_err};
11use diagnostics_hierarchy::{
12 ArrayContent, ArrayFormat, ExponentialHistogram, ExponentialHistogramParams, LinearHistogram,
13 LinearHistogramParams, Property,
14};
15use difference::{Changeset, Difference};
16use num_traits::One;
17use regex::Regex;
18use std::collections::BTreeSet;
19use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
20use std::ops::{Add, AddAssign, MulAssign};
21
22pub use diagnostics_hierarchy::{DiagnosticsHierarchy, DiagnosticsHierarchyGetter, hierarchy};
23pub use std::sync::Arc;
24
25pub trait JsonGetter<K: Clone + AsRef<str>>: DiagnosticsHierarchyGetter<K> {
26 fn get_pretty_json(&self) -> impl std::future::Future<Output = String> {
27 async {
28 let mut tree = self.get_diagnostics_hierarchy().await;
29 tree.to_mut().sort();
30 serde_json::to_string_pretty(&tree).expect("pretty json string")
31 }
32 }
33
34 fn get_json(&self) -> impl std::future::Future<Output = String> {
35 async {
36 let mut tree = self.get_diagnostics_hierarchy().await;
37 tree.to_mut().sort();
38 serde_json::to_string(&tree).expect("json string")
39 }
40 }
41}
42
43impl<K: Clone + AsRef<str>, T: DiagnosticsHierarchyGetter<K>> JsonGetter<K> for T {}
44
45#[macro_export]
74macro_rules! tree_assertion {
75 (@build $tree_assertion:expr,) => {};
76
77 (@build $tree_assertion:expr, var $key:ident: { $($sub:tt)* }) => {{
79 #[allow(unused_mut)]
80 let mut child_tree_assertion = TreeAssertion::new($key, true);
81 $crate::tree_assertion!(@build child_tree_assertion, $($sub)*);
82 $tree_assertion.add_child_assertion(child_tree_assertion);
83 }};
84 (@build $tree_assertion:expr, var $key:ident: { $($sub:tt)* }, $($rest:tt)*) => {{
85 $crate::tree_assertion!(@build $tree_assertion, var $key: { $($sub)* });
86 $crate::tree_assertion!(@build $tree_assertion, $($rest)*);
87 }};
88
89 (@build $tree_assertion:expr, var $key:ident: contains { $($sub:tt)* }) => {{
91 #[allow(unused_mut)]
92 let mut child_tree_assertion = TreeAssertion::new($key, false);
93 $crate::tree_assertion!(@build child_tree_assertion, $($sub)*);
94 $tree_assertion.add_child_assertion(child_tree_assertion);
95 }};
96 (@build $tree_assertion:expr, var $key:ident: contains { $($sub:tt)* }, $($rest:tt)*) => {{
97 $crate::tree_assertion!(@build $tree_assertion, var $key: contains { $($sub)* });
98 $crate::tree_assertion!(@build $tree_assertion, $($rest)*);
99 }};
100
101 (@build $tree_assertion:expr, var $key:ident: $assertion:expr) => {{
103 $tree_assertion.add_property_assertion($key, $crate::Arc::new($assertion))
104 }};
105 (@build $tree_assertion:expr, var $key:ident: $assertion:expr, $($rest:tt)*) => {{
106 $crate::tree_assertion!(@build $tree_assertion, var $key: $assertion);
107 $crate::tree_assertion!(@build $tree_assertion, $($rest)*);
108 }};
109
110 (@build $tree_assertion:expr, $key:ident: $($rest:tt)+) => {{
113 let key = stringify!($key);
114 $crate::tree_assertion!(@build $tree_assertion, var key: $($rest)+);
115 }};
116 (@build $tree_assertion:expr, ref $key:path: $($rest:tt)+) => {{
122 let key: &str = $key.as_ref(); $crate::tree_assertion!(@build $tree_assertion, var key: $($rest)+);
124 }};
125 (@build $tree_assertion:expr, $key:tt: $($rest:tt)+) => {{
127 let key: &'static str = $key;
128 $crate::tree_assertion!(@build $tree_assertion, var key: $($rest)+);
129 }};
130 (@build $tree_assertion:expr, $key:expr => $($rest:tt)+) => {{
132 let key_string : String = $key;
133 let key = &key_string;
134 $crate::tree_assertion!(@build $tree_assertion, var key: $($rest)+);
135 }};
136 (@build $tree_assertion:expr, $child_assertion:expr, $($rest:tt)*) => {{
138 $tree_assertion.add_child_assertion($child_assertion);
139 $crate::tree_assertion!(@build $tree_assertion, $($rest)*);
140 }};
141
142 (var $key:ident: { $($sub:tt)* }) => {{
144 use $crate::TreeAssertion;
145 #[allow(unused_mut)]
146 let mut tree_assertion = TreeAssertion::new($key, true);
147 $crate::tree_assertion!(@build tree_assertion, $($sub)*);
148 tree_assertion
149 }};
150 (var $key:ident: contains { $($sub:tt)* }) => {{
151 use $crate::TreeAssertion;
152 #[allow(unused_mut)]
153 let mut tree_assertion = TreeAssertion::new($key, false);
154 $crate::tree_assertion!(@build tree_assertion, $($sub)*);
155 tree_assertion
156 }};
157 ($key:ident: $($rest:tt)+) => {{
158 let key = stringify!($key);
159 $crate::tree_assertion!(var key: $($rest)+)
160 }};
161 ($key:tt: $($rest:tt)+) => {{
162 let key: &'static str = $key;
163 $crate::tree_assertion!(var key: $($rest)+)
164 }};
165}
166
167#[macro_export]
254macro_rules! assert_data_tree {
255 (@executor $exec:expr, $diagnostics_hierarchy:expr, $($rest:tt)+) => {{
256 use $crate::DiagnosticsHierarchyGetter as _;
257 let tree_assertion = $crate::tree_assertion!($($rest)+);
258
259 let mut fut = core::pin::pin!($diagnostics_hierarchy.get_diagnostics_hierarchy());
260 let hierarchy = loop {
261 if let core::task::Poll::Ready(h) = $exec.run_until_stalled(&mut fut) {
262 break h;
263 }
264
265 $exec.wake_next_timer();
266 };
267
268 if let Err(e) = tree_assertion.run(hierarchy.as_ref()) {
269 panic!("tree assertion fails: {}", e);
270 }
271 }};
272 (@retry $times:expr, $diagnostics_hierarchy:expr, $($rest:tt)+) => {{
273 use $crate::DiagnosticsHierarchyGetter as _;
274 use fuchsia_async as fasync;
275 use zx::MonotonicDuration;
276
277 let tree_assertion = $crate::tree_assertion!($($rest)+);
278 let max_retries = $times;
279
280 for i in 0..max_retries {
281 let result = tree_assertion.run(
282 $diagnostics_hierarchy.get_diagnostics_hierarchy().await.as_ref()
283 );
284
285 if result.is_ok() {
286 break;
287 } else if i == (max_retries - 1) && result.is_err() {
288 panic!("tree assertion fails: {}", result.unwrap_err());
289 }
290
291 fasync::Timer::new(MonotonicDuration::from_seconds(1)).await;
292 }
293
294 }};
295 ($diagnostics_hierarchy:expr, $($rest:tt)+) => {{
296 use $crate::DiagnosticsHierarchyGetter as _;
297 let tree_assertion = $crate::tree_assertion!($($rest)+);
298
299 if let Err(e) = tree_assertion.run(
300 $diagnostics_hierarchy.get_diagnostics_hierarchy().await.as_ref()
301 ) {
302 panic!("tree assertion fails: {}", e);
303 }
304 }};
305}
306
307#[macro_export]
312macro_rules! assert_json_diff {
313 ($diagnostics_hierarchy:expr, $($rest:tt)+) => {{
314 use $crate::JsonGetter as _;
315 let expected = $diagnostics_hierarchy.get_pretty_json().await;
316 let actual_hierarchy: $crate::DiagnosticsHierarchy = $crate::hierarchy!{$($rest)+};
317 let actual = actual_hierarchy.get_pretty_json().await;
318
319 if actual != expected {
320 panic!("{}", $crate::Diff::from_text(&expected, &actual));
321 }
322 }}
323}
324
325pub struct Diff(Changeset);
327
328impl Diff {
329 fn new(expected: &dyn Debug, actual: &dyn Debug) -> Self {
330 let expected = format!("{expected:#?}");
331 let actual = format!("{actual:#?}");
332 Self::from_text(&expected, &actual)
333 }
334
335 #[doc(hidden)]
336 pub fn from_text(expected: &str, actual: &str) -> Self {
337 Diff(Changeset::new(expected, actual, "\n"))
338 }
339}
340
341impl Display for Diff {
342 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
343 writeln!(f, "(-) Expected vs. (+) Actual:")?;
344 for diff in &self.0.diffs {
345 let (prefix, contents) = match diff {
346 Difference::Same(same) => (" ", same),
347 Difference::Add(added) => ("+ ", added),
348 Difference::Rem(removed) => ("- ", removed),
349 };
350 for line in contents.split("\n") {
351 writeln!(f, "{prefix}{line}")?;
352 }
353 }
354 Ok(())
355 }
356}
357
358macro_rules! eq_or_bail {
359 ($expected:expr, $actual:expr) => {{
360 if $expected != $actual {
361 let changes = Diff::new(&$expected, &$actual);
362 return Err(format_err!("\n {}", changes));
363 }
364 }};
365 ($expected:expr, $actual:expr, $($args:tt)+) => {{
366 if $expected != $actual {
367 let changes = Diff::new(&$expected, &$actual);
368 return Err(format_err!("{}:\n {}", format!($($args)+), changes));
369 }
370 }}
371}
372
373pub struct TreeAssertion<K = String> {
375 name: String,
377 path: String,
380 properties: Vec<(String, Arc<dyn PropertyAssertion<K>>)>,
382 children: Vec<TreeAssertion<K>>,
384 exact_match: bool,
386}
387
388impl<K> TreeAssertion<K>
389where
390 K: AsRef<str>,
391{
392 pub fn new(name: &str, exact_match: bool) -> Self {
396 Self {
397 name: name.to_string(),
398 path: name.to_string(),
399 properties: vec![],
400 children: vec![],
401 exact_match,
402 }
403 }
404
405 pub fn add_property_assertion(&mut self, key: &str, assertion: Arc<dyn PropertyAssertion<K>>) {
407 self.properties.push((key.to_owned(), assertion));
408 }
409
410 pub fn add_child_assertion(&mut self, mut assertion: TreeAssertion<K>) {
412 assertion.path = format!("{}.{}", self.path, assertion.name);
413 self.children.push(assertion);
414 }
415
416 pub fn run(&self, actual: &DiagnosticsHierarchy<K>) -> Result<(), Error> {
419 eq_or_bail!(self.name, actual.name, "node `{}` - expected node name != actual", self.path);
420
421 if self.exact_match {
422 let properties_names = self.properties.iter().map(|p| p.0.to_string());
423 let children_names = self.children.iter().map(|c| c.name.to_string());
424 let keys: BTreeSet<String> = properties_names.chain(children_names).collect();
425
426 let actual_props = actual.properties.iter().map(|p| p.name().to_string());
427 let actual_children = actual.children.iter().map(|c| c.name.to_string());
428 let actual_keys: BTreeSet<String> = actual_props.chain(actual_children).collect();
429 eq_or_bail!(keys, actual_keys, "node `{}` - expected keys != actual", self.path);
430 }
431
432 for (name, assertion) in self.properties.iter() {
433 let mut matched = actual.properties.iter().filter(|p| p.key().as_ref() == name);
434 let first_match = matched.next();
435 if let Some(_second_match) = matched.next() {
436 bail!("node `{}` - multiple properties found with name `{}`", self.path, name);
437 }
438 match first_match {
439 Some(property) => {
440 if let Err(e) = assertion.run(property) {
441 bail!(
442 "node `{}` - assertion fails for property `{}`. Reason: {}",
443 self.path,
444 name,
445 e
446 );
447 }
448 }
449 None => {
450 let mut child_match =
451 actual.children.iter().filter(|c| &c.name == name).peekable();
452 if child_match.peek().is_some() {
453 bail!(
454 "node `{}` - no property named `{}`, but there is a node. Use `{name}: contains {{}}` to test existence of a node",
455 self.path,
456 name
457 )
458 } else {
459 bail!("node `{}` - no property named `{}`", self.path, name)
460 }
461 }
462 }
463 }
464 for assertion in self.children.iter() {
465 let mut matched = actual.children.iter().filter(|c| c.name == assertion.name);
466 let first_match = matched.next();
467 if let Some(_second_match) = matched.next() {
468 bail!(
469 "node `{}` - multiple children found with name `{}`",
470 self.path,
471 assertion.name
472 );
473 }
474 match first_match {
475 Some(child) => assertion.run(child)?,
476 None => bail!("node `{}` - no child named `{}`", self.path, assertion.name),
477 }
478 }
479 Ok(())
480 }
481}
482
483pub trait PropertyAssertion<K = String>: Send + Sync {
485 fn run(&self, actual: &Property<K>) -> Result<(), Error>;
488}
489
490macro_rules! impl_property_assertion {
491 ($prop_variant:ident, $($ty:ty),+) => {
492 $(
493 impl<K> PropertyAssertion<K> for $ty {
494 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
495 if let Property::$prop_variant(_key, value, ..) = actual {
496 eq_or_bail!(self, value);
497 } else {
498 return Err(format_err!("expected {}, found {}",
499 stringify!($prop_variant), actual.discriminant_name()));
500 }
501 Ok(())
502 }
503 }
504 )+
505 };
506
507 ($prop_variant:ident, $cast:ty; $($ty:ty),+) => {
508 $(
509 impl<K> PropertyAssertion<K> for $ty {
510 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
511 if let Property::$prop_variant(_key, value, ..) = actual {
512 eq_or_bail!(*self as $cast, *value);
513 } else {
514 return Err(format_err!("expected {}, found {}",
515 stringify!($prop_variant), actual.discriminant_name()));
516 }
517 Ok(())
518 }
519 }
520 )+
521 }
522}
523
524macro_rules! impl_array_properties_assertion {
525 ($prop_variant:ident, $($ty:ty),+) => {
526 $(
527 impl<K> PropertyAssertion<K> for Vec<$ty> {
529 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
530 if let Property::$prop_variant(_key, value, ..) = actual {
531 match &value {
532 ArrayContent::Values(values) => eq_or_bail!(self, values),
533 _ => {
534 return Err(format_err!(
535 "expected a plain {} array, got a {}",
536 stringify!($prop_variant),
537 actual.discriminant_name()
538 ));
539 }
540 }
541 } else {
542 return Err(format_err!("expected {}, found {}",
543 stringify!($prop_variant), actual.discriminant_name()));
544 }
545 Ok(())
546 }
547 }
548
549 impl<K> PropertyAssertion<K> for LinearHistogram<$ty> {
550 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
551 if let Property::$prop_variant(_key, value, ..) = actual {
552 match &value {
553 ArrayContent::LinearHistogram(histogram) => {
554 eq_or_bail!(self, histogram)
555 }
556 _ => {
557 return Err(format_err!(
558 "expected a linear {} histogram, got a {}",
559 stringify!($prop_variant),
560 actual.discriminant_name()
561 ));
562 }
563 }
564 } else {
565 return Err(format_err!("expected {}, found {}",
566 stringify!($prop_variant), actual.discriminant_name()));
567 }
568 Ok(())
569 }
570 }
571
572 impl<K> PropertyAssertion<K> for ExponentialHistogram<$ty> {
573 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
574 if let Property::$prop_variant(_key, value, ..) = actual {
575 match &value {
576 ArrayContent::ExponentialHistogram(histogram) => {
577 eq_or_bail!(self, histogram)
578 }
579 _ => {
580 return Err(format_err!(
581 "expected an exponential {} histogram, got a {}",
582 stringify!($prop_variant),
583 actual.discriminant_name()
584 ));
585 }
586 }
587 } else {
588 return Err(format_err!("expected {}, found {}",
589 stringify!($prop_variant), actual.discriminant_name()));
590 }
591 Ok(())
592 }
593 }
594
595 impl<K> PropertyAssertion<K> for HistogramAssertion<$ty> {
597 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
598 if let Property::$prop_variant(_key, value, ..) = actual {
599 let expected_content =
600 ArrayContent::new(self.values.clone(), self.format.clone()).map_err(
601 |e| {
602 format_err!(
603 "failed to load array content for expected assertion {}: {:?}",
604 stringify!($prop_variant),
605 e
606 )
607 },
608 )?;
609 eq_or_bail!(&expected_content, value);
610 } else {
611 return Err(format_err!(
612 "expected {}, found {}",
613 stringify!($prop_variant),
614 actual.discriminant_name(),
615 ));
616 }
617 Ok(())
618 }
619 }
620 )+
621 }
622}
623
624impl_property_assertion!(String, &str, String);
625impl_property_assertion!(Bytes, Vec<u8>);
626impl_property_assertion!(Uint, u64; u64, u32, u16, u8);
627impl_property_assertion!(Int, i64; i64, i32, i16, i8);
628impl_property_assertion!(Double, f64; f64, f32);
629impl_property_assertion!(Bool, bool);
630impl_array_properties_assertion!(DoubleArray, f64);
631impl_array_properties_assertion!(IntArray, i64);
632impl_array_properties_assertion!(UintArray, u64);
633
634pub struct AnyProperty;
636
637impl<K> PropertyAssertion<K> for AnyProperty {
638 fn run(&self, _actual: &Property<K>) -> Result<(), Error> {
639 Ok(())
640 }
641}
642
643pub struct AnyStringProperty;
645
646impl<K> PropertyAssertion<K> for AnyStringProperty {
647 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
648 match actual {
649 Property::String(..) => Ok(()),
650 _ => Err(format_err!("expected String, found {}", actual.discriminant_name())),
651 }
652 }
653}
654
655impl<K> PropertyAssertion<K> for Regex {
658 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
659 (&self).run(actual)
660 }
661}
662
663impl<K> PropertyAssertion<K> for &Regex {
664 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
665 if let Property::String(_, v) = actual {
666 if self.is_match(v) {
667 return Ok(());
668 } else {
669 return Err(format_err!(
670 "expected String matching \"{}\", found \"{}\"",
671 self.as_str(),
672 v
673 ));
674 }
675 }
676 Err(format_err!(
677 "expected String matching \"{}\", found {}",
678 self.as_str(),
679 actual.discriminant_name()
680 ))
681 }
682}
683
684pub struct AnyBytesProperty;
686
687impl<K> PropertyAssertion<K> for AnyBytesProperty {
688 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
689 match actual {
690 Property::Bytes(..) => Ok(()),
691 _ => Err(format_err!("expected bytes, found {}", actual.discriminant_name())),
692 }
693 }
694}
695
696pub struct AnyUintProperty;
698
699impl<K> PropertyAssertion<K> for AnyUintProperty {
700 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
701 match actual {
702 Property::Uint(..) => Ok(()),
703 _ => Err(format_err!("expected Uint, found {}", actual.discriminant_name())),
704 }
705 }
706}
707
708pub struct NonZeroUintProperty;
712
713impl<K> PropertyAssertion<K> for NonZeroUintProperty {
714 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
715 match actual {
716 Property::Uint(_, v) if *v != 0 => Ok(()),
717 Property::Uint(_, v) if *v == 0 => {
718 Err(format_err!("expected non-zero integer, found 0"))
719 }
720 _ => {
721 Err(format_err!("expected non-zero integer, found {}", actual.discriminant_name()))
722 }
723 }
724 }
725}
726
727pub struct NonZeroIntProperty;
731
732impl<K> PropertyAssertion<K> for NonZeroIntProperty {
733 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
734 match actual {
735 Property::Int(_, v) if *v != 0 => Ok(()),
736 Property::Int(_, v) if *v == 0 => {
737 Err(format_err!("expected non-zero signed integer, found 0"))
738 }
739 _ => Err(format_err!(
740 "expected non-zero signed integer, found {}",
741 actual.discriminant_name()
742 )),
743 }
744 }
745}
746
747pub struct AnyIntProperty;
749
750impl<K> PropertyAssertion<K> for AnyIntProperty {
751 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
752 match actual {
753 Property::Int(..) => Ok(()),
754 _ => Err(format_err!("expected Int, found {}", actual.discriminant_name())),
755 }
756 }
757}
758
759pub struct AnyDoubleProperty;
761
762impl<K> PropertyAssertion<K> for AnyDoubleProperty {
763 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
764 match actual {
765 Property::Double(..) => Ok(()),
766 _ => Err(format_err!("expected Double, found {}", actual.discriminant_name())),
767 }
768 }
769}
770
771pub struct AnyNumericProperty;
773
774impl<K> PropertyAssertion<K> for AnyNumericProperty {
775 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
776 match actual {
777 Property::Int(..) | Property::Uint(..) | Property::Double(..) => Ok(()),
778 _ => Err(format_err!(
779 "expected an Int, Uint or Double. found {}",
780 actual.discriminant_name()
781 )),
782 }
783 }
784}
785
786pub struct AnyBoolProperty;
788
789impl<K> PropertyAssertion<K> for AnyBoolProperty {
790 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
791 match actual {
792 Property::Bool(..) => Ok(()),
793 _ => Err(format_err!("expected Bool, found {}", actual.discriminant_name())),
794 }
795 }
796}
797
798impl<K> PropertyAssertion<K> for Vec<String> {
799 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
800 let this = self.iter().map(|s| s.as_ref()).collect::<Vec<&str>>();
801 this.run(actual)
802 }
803}
804
805impl<K> PropertyAssertion<K> for Vec<&str> {
806 fn run(&self, actual: &Property<K>) -> Result<(), Error> {
807 match actual {
808 Property::StringList(_key, value) => {
809 eq_or_bail!(self, value);
810 Ok(())
811 }
812 _ => Err(format_err!("expected StringList, found {}", actual.discriminant_name())),
813 }
814 }
815}
816
817#[derive(Clone)]
819pub struct HistogramAssertion<T> {
820 format: ArrayFormat,
821 values: Vec<T>,
822}
823
824impl<T: MulAssign + AddAssign + PartialOrd + Add<Output = T> + Copy + Default + One>
825 HistogramAssertion<T>
826{
827 pub fn linear(params: LinearHistogramParams<T>) -> Self {
829 let mut values =
830 vec![T::default(); params.buckets + ArrayFormat::LinearHistogram.extra_slots()];
831 values[0] = params.floor;
832 values[1] = params.step_size;
833 Self { format: ArrayFormat::LinearHistogram, values }
834 }
835
836 pub fn exponential(params: ExponentialHistogramParams<T>) -> Self {
838 let mut values =
839 vec![T::default(); params.buckets + ArrayFormat::ExponentialHistogram.extra_slots()];
840 values[0] = params.floor;
841 values[1] = params.initial_step;
842 values[2] = params.step_multiplier;
843 Self { format: ArrayFormat::ExponentialHistogram, values }
844 }
845
846 pub fn insert_values(&mut self, values: impl IntoIterator<Item = T>) {
848 match self.format {
849 ArrayFormat::ExponentialHistogram => {
850 for value in values {
851 self.insert_exp(value);
852 }
853 }
854 ArrayFormat::LinearHistogram => {
855 for value in values {
856 self.insert_linear(value);
857 }
858 }
859 ArrayFormat::Default => {
860 unreachable!("can't construct a histogram assertion for arrays");
861 }
862 }
863 }
864
865 fn insert_linear(&mut self, value: T) {
866 let value_index = {
867 let mut current_floor = self.values[0];
868 let step_size = self.values[1];
869 let mut index = ArrayFormat::LinearHistogram.underflow_bucket_index();
870 while value >= current_floor && index < self.values.len() - 1 {
871 current_floor += step_size;
872 index += 1;
873 }
874 index
875 };
876 self.values[value_index] += T::one();
877 }
878
879 fn insert_exp(&mut self, value: T) {
880 let value_index = {
881 let floor = self.values[0];
882 let mut current_floor = self.values[0];
883 let mut offset = self.values[1];
884 let step_multiplier = self.values[2];
885 let mut index = ArrayFormat::ExponentialHistogram.underflow_bucket_index();
887 while value >= current_floor && index < self.values.len() - 1 {
888 current_floor = floor + offset;
889 offset *= step_multiplier;
890 index += 1;
891 }
892 index
893 };
894 self.values[value_index] += T::one();
895 }
896}
897
898#[cfg(test)]
899mod tests {
900 use super::*;
901 use crate::Difference::{Add, Rem, Same};
902 use diagnostics_hierarchy::testing::CondensableOnDemand;
903 use std::sync::LazyLock;
904
905 static TEST_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new("a").unwrap());
906
907 #[fuchsia::test]
908 fn keep_tree_assertion_send_sync() {
909 fn assert_send_sync<T: Send + Sync>() {}
910 assert_send_sync::<TreeAssertion>();
911 }
912
913 #[fuchsia::test]
914 async fn test_assert_json_diff() {
915 assert_json_diff!(
916 simple_tree(),
917 key: {
918 sub: "sub_value",
919 sub2: "sub2_value",
920 }
921 );
922
923 let diagnostics_hierarchy = complex_tree();
924 assert_json_diff!(diagnostics_hierarchy, key: {
925 sub: "sub_value",
926 sub2: "sub2_value",
927 child1: {
928 child1_sub: 10,
929 },
930 child2: {
931 child2_sub: 20,
932 },
933 });
934 }
935
936 #[fuchsia::test]
937 #[should_panic]
938 async fn test_panicking_assert_json_diff() {
939 assert_json_diff!(
940 simple_tree(),
941 key: {
942 sub: "sub_value",
943 sb2: "sub2_value",
944 }
945 );
946
947 let diagnostics_hierarchy = complex_tree();
948 assert_json_diff!(diagnostics_hierarchy, key: {
949 sb: "sub_value",
950 sub2: "sub2_value",
951 child: {
952 child1_sub: 10,
953 },
954 child3: {
955 child2_sub: 20,
956 },
957 });
958 }
959
960 #[fuchsia::test]
961 async fn test_exact_match_simple() {
962 let diagnostics_hierarchy = simple_tree();
963 assert_data_tree!(diagnostics_hierarchy, key: {
964 sub: "sub_value",
965 sub2: "sub2_value",
966 });
967 }
968
969 #[fuchsia::test]
970 async fn test_exact_match_complex() {
971 let diagnostics_hierarchy = complex_tree();
972 assert_data_tree!(diagnostics_hierarchy, key: {
973 sub: "sub_value",
974 sub2: "sub2_value",
975 child1: {
976 child1_sub: 10,
977 },
978 child2: {
979 child2_sub: 20u64,
980 },
981 });
982 }
983
984 #[fuchsia::test]
985 #[should_panic]
986 async fn test_exact_match_mismatched_property_name() {
987 let diagnostics_hierarchy = simple_tree();
988 assert_data_tree!(diagnostics_hierarchy, key: {
989 sub: "sub_value",
990 sub3: "sub2_value",
991 });
992 }
993
994 #[fuchsia::test]
995 #[should_panic]
996 async fn test_exact_match_mismatched_child_name() {
997 let diagnostics_hierarchy = complex_tree();
998 assert_data_tree!(diagnostics_hierarchy, key: {
999 sub: "sub_value",
1000 sub2: "sub2_value",
1001 child1: {
1002 child1_sub: 10,
1003 },
1004 child3: {
1005 child2_sub: 20,
1006 },
1007 });
1008 }
1009
1010 #[fuchsia::test]
1011 #[should_panic]
1012 async fn test_exact_match_mismatched_property_name_in_child() {
1013 let diagnostics_hierarchy = complex_tree();
1014 assert_data_tree!(diagnostics_hierarchy, key: {
1015 sub: "sub_value",
1016 sub2: "sub2_value",
1017 child1: {
1018 child2_sub: 10,
1019 },
1020 child2: {
1021 child2_sub: 20,
1022 },
1023 });
1024 }
1025
1026 #[fuchsia::test]
1027 #[should_panic]
1028 async fn test_exact_match_mismatched_property_value() {
1029 let diagnostics_hierarchy = simple_tree();
1030 assert_data_tree!(diagnostics_hierarchy, key: {
1031 sub: "sub2_value",
1032 sub2: "sub2_value",
1033 });
1034 }
1035
1036 #[fuchsia::test]
1037 #[should_panic]
1038 async fn test_exact_match_missing_property() {
1039 let diagnostics_hierarchy = simple_tree();
1040 assert_data_tree!(diagnostics_hierarchy, key: {
1041 sub: "sub_value",
1042 });
1043 }
1044
1045 #[fuchsia::test]
1046 #[should_panic]
1047 async fn test_exact_match_missing_child() {
1048 let diagnostics_hierarchy = complex_tree();
1049 assert_data_tree!(diagnostics_hierarchy, key: {
1050 sub: "sub_value",
1051 sub2: "sub2_value",
1052 child1: {
1053 child1_sub: 10,
1054 },
1055 });
1056 }
1057
1058 #[fuchsia::test]
1059 async fn test_partial_match_success() {
1060 let diagnostics_hierarchy = complex_tree();
1061
1062 assert_data_tree!(diagnostics_hierarchy, key: contains {});
1064
1065 assert_data_tree!(diagnostics_hierarchy, key: contains {
1067 sub: "sub_value",
1068 child1: contains {},
1069 });
1070 }
1071
1072 #[fuchsia::test]
1073 #[should_panic]
1074 async fn test_partial_match_nonexistent_property() {
1075 let diagnostics_hierarchy = simple_tree();
1076 assert_data_tree!(diagnostics_hierarchy, key: contains {
1077 sub3: AnyProperty,
1078 });
1079 }
1080
1081 #[fuchsia::test]
1082 async fn test_ignore_property_value() {
1083 let diagnostics_hierarchy = simple_tree();
1084 assert_data_tree!(diagnostics_hierarchy, key: {
1085 sub: AnyProperty,
1086 sub2: "sub2_value",
1087 });
1088 }
1089
1090 #[fuchsia::test]
1091 #[should_panic]
1092 async fn test_ignore_property_value_property_name_is_still_checked() {
1093 let diagnostics_hierarchy = simple_tree();
1094 assert_data_tree!(diagnostics_hierarchy, key: {
1095 sub1: AnyProperty,
1096 sub2: "sub2_value",
1097 })
1098 }
1099
1100 #[fuchsia::test]
1101 async fn test_expr_key_syntax() {
1102 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1103 "key",
1104 vec![Property::String("@time".to_string(), "1.000".to_string())],
1105 vec![],
1106 );
1107 assert_data_tree!(diagnostics_hierarchy, key: {
1108 "@time": "1.000"
1109 });
1110 }
1111
1112 #[fuchsia::test]
1113 async fn test_var_key_syntax() {
1114 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1115 "key",
1116 vec![Property::String("@time".to_string(), "1.000".to_string())],
1117 vec![],
1118 );
1119 let time_key = "@time";
1120 assert_data_tree!(diagnostics_hierarchy, key: {
1121 var time_key: "1.000"
1122 });
1123 }
1124
1125 const KEY_AS_STR: &str = "@time";
1126
1127 #[fuchsia::test]
1129 async fn test_path_key_syntax() {
1130 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1131 "key",
1132 vec![Property::String("@time".to_string(), "1.000".to_string())],
1133 vec![],
1134 );
1135 assert_data_tree!(diagnostics_hierarchy, key: {
1136 ref crate::tests::KEY_AS_STR: "1.000"
1138 });
1139 }
1140
1141 #[fuchsia::test]
1142 async fn test_arrays() {
1143 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1144 "key",
1145 vec![
1146 Property::UintArray("@uints".to_string(), ArrayContent::Values(vec![1, 2, 3])),
1147 Property::IntArray("@ints".to_string(), ArrayContent::Values(vec![-2, -4, 0])),
1148 Property::DoubleArray(
1149 "@doubles".to_string(),
1150 ArrayContent::Values(vec![1.3, 2.5, -3.6]),
1151 ),
1152 ],
1153 vec![],
1154 );
1155 assert_data_tree!(diagnostics_hierarchy, key: {
1156 "@uints": vec![1u64, 2, 3],
1157 "@ints": vec![-2i64, -4, 0],
1158 "@doubles": vec![1.3, 2.5, -3.6]
1159 });
1160 }
1161
1162 #[fuchsia::test]
1163 async fn test_histograms() {
1164 let mut condensed_int_histogram =
1165 ArrayContent::new(vec![6, 7, 0, 9, 0], ArrayFormat::LinearHistogram).unwrap();
1166 condensed_int_histogram.condense_histogram();
1167 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1168 "key",
1169 vec![
1170 Property::UintArray(
1171 "@linear-uints".to_string(),
1172 ArrayContent::new(vec![1, 2, 3, 4, 5], ArrayFormat::LinearHistogram).unwrap(),
1173 ),
1174 Property::IntArray(
1175 "@linear-ints".to_string(),
1176 ArrayContent::new(vec![6, 7, 8, 9, 10], ArrayFormat::LinearHistogram).unwrap(),
1177 ),
1178 Property::IntArray("@linear-sparse-ints".to_string(), condensed_int_histogram),
1179 Property::DoubleArray(
1180 "@linear-doubles".to_string(),
1181 ArrayContent::new(vec![1.0, 2.0, 4.0, 5.0, 6.0], ArrayFormat::LinearHistogram)
1182 .unwrap(),
1183 ),
1184 Property::UintArray(
1185 "@exp-uints".to_string(),
1186 ArrayContent::new(vec![2, 4, 6, 8, 10, 12], ArrayFormat::ExponentialHistogram)
1187 .unwrap(),
1188 ),
1189 Property::IntArray(
1190 "@exp-ints".to_string(),
1191 ArrayContent::new(vec![1, 3, 5, 7, 9, 11], ArrayFormat::ExponentialHistogram)
1192 .unwrap(),
1193 ),
1194 Property::DoubleArray(
1195 "@exp-doubles".to_string(),
1196 ArrayContent::new(
1197 vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0],
1198 ArrayFormat::ExponentialHistogram,
1199 )
1200 .unwrap(),
1201 ),
1202 ],
1203 vec![],
1204 );
1205 let mut linear_uint_assertion = HistogramAssertion::linear(LinearHistogramParams {
1206 floor: 1u64,
1207 step_size: 2,
1208 buckets: 1,
1209 });
1210 linear_uint_assertion.insert_values(vec![0, 0, 0, 2, 2, 2, 2, 4, 4, 4, 4, 4]);
1211 let mut exponential_double_assertion =
1212 HistogramAssertion::exponential(ExponentialHistogramParams {
1213 floor: 1.0,
1214 initial_step: 2.0,
1215 step_multiplier: 3.0,
1216 buckets: 1,
1217 });
1218 exponential_double_assertion.insert_values(vec![
1219 -3.1, -2.2, -1.3, 0.0, 1.1, 1.2, 2.5, 2.8, 2.0, 3.1, 4.2, 5.3, 6.4, 7.5, 8.6,
1220 ]);
1221 assert_data_tree!(diagnostics_hierarchy, key: {
1222 "@linear-uints": linear_uint_assertion,
1223 "@linear-ints": LinearHistogram {
1224 floor: 6i64,
1225 step: 7,
1226 counts: vec![8, 9, 10],
1227 indexes: None,
1228 size: 3
1229 },
1230 "@linear-sparse-ints": LinearHistogram {
1231 floor: 6i64,
1232 step: 7,
1233 counts: vec![9],
1234 indexes: Some(vec![1]),
1235 size: 3
1236 },
1237 "@linear-doubles": LinearHistogram {
1238 floor: 1.0,
1239 step: 2.0,
1240 counts: vec![4.0, 5.0, 6.0],
1241 indexes: None,
1242 size: 3
1243 },
1244 "@exp-uints": ExponentialHistogram {
1245 floor: 2u64,
1246 initial_step: 4,
1247 step_multiplier: 6,
1248 counts: vec![8, 10, 12],
1249 indexes: None,
1250 size: 3
1251 },
1252 "@exp-ints": ExponentialHistogram {
1253 floor: 1i64,
1254 initial_step: 3,
1255 step_multiplier: 5,
1256 counts: vec![7,9,11],
1257 indexes: None,
1258 size: 3
1259 },
1260 "@exp-doubles": exponential_double_assertion,
1261 });
1262 }
1263
1264 #[fuchsia::test]
1265 async fn test_matching_tree_assertion_expression() {
1266 let diagnostics_hierarchy = complex_tree();
1267 let child1 = tree_assertion!(
1268 child1: {
1269 child1_sub: 10,
1270 }
1271 );
1272 assert_data_tree!(diagnostics_hierarchy, key: {
1273 sub: "sub_value",
1274 sub2: "sub2_value",
1275 child1,
1276 tree_assertion!(
1277 child2: {
1278 child2_sub: 20u64,
1279 }
1280 ),
1281 });
1282 }
1283
1284 #[fuchsia::test]
1285 #[should_panic]
1286 async fn test_matching_non_unique_property_fails() {
1287 let diagnostics_hierarchy = non_unique_prop_tree();
1288 assert_data_tree!(diagnostics_hierarchy, key: { prop: "prop_value#0" });
1289 }
1290
1291 #[fuchsia::test]
1292 #[should_panic]
1293 async fn test_matching_non_unique_property_fails_2() {
1294 let diagnostics_hierarchy = non_unique_prop_tree();
1295 assert_data_tree!(diagnostics_hierarchy, key: { prop: "prop_value#1" });
1296 }
1297
1298 #[fuchsia::test]
1299 #[should_panic]
1300 async fn test_matching_non_unique_property_fails_3() {
1301 let diagnostics_hierarchy = non_unique_prop_tree();
1302 assert_data_tree!(diagnostics_hierarchy, key: {
1303 prop: "prop_value#0",
1304 prop: "prop_value#1",
1305 });
1306 }
1307
1308 #[fuchsia::test]
1309 #[should_panic]
1310 async fn test_matching_non_unique_child_fails() {
1311 let diagnostics_hierarchy = non_unique_child_tree();
1312 assert_data_tree!(diagnostics_hierarchy, key: {
1313 child: {
1314 prop: 10
1315 }
1316 });
1317 }
1318
1319 #[fuchsia::test]
1320 #[should_panic]
1321 async fn test_matching_non_unique_child_fails_2() {
1322 let diagnostics_hierarchy = non_unique_child_tree();
1323 assert_data_tree!(diagnostics_hierarchy, key: {
1324 child: {
1325 prop: 20
1326 }
1327 });
1328 }
1329
1330 #[fuchsia::test]
1331 #[should_panic]
1332 async fn test_matching_non_unique_child_fails_3() {
1333 let diagnostics_hierarchy = non_unique_child_tree();
1334 assert_data_tree!(diagnostics_hierarchy, key: {
1335 child: {
1336 prop: 10,
1337 },
1338 child: {
1339 prop: 20,
1340 },
1341 });
1342 }
1343
1344 #[fuchsia::test]
1345 async fn test_any_string_property_passes() {
1346 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1347 "key",
1348 vec![
1349 Property::String("value1".to_string(), "a".to_string()),
1350 Property::String("value2".to_string(), "b".to_string()),
1351 ],
1352 vec![],
1353 );
1354 assert_data_tree!(diagnostics_hierarchy, key: {
1355 value1: AnyStringProperty,
1356 value2: AnyStringProperty,
1357 });
1358 }
1359
1360 #[fuchsia::test]
1361 #[should_panic]
1362 async fn test_any_string_property_fails() {
1363 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1364 "key",
1365 vec![Property::Int("value1".to_string(), 10i64)],
1366 vec![],
1367 );
1368 assert_data_tree!(diagnostics_hierarchy, key: {
1369 value1: AnyStringProperty,
1370 });
1371 }
1372
1373 #[fuchsia::test]
1374 async fn test_string_property_regex_passes() {
1375 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1376 "key",
1377 vec![Property::String("value1".to_string(), "aaaabbbb".to_string())],
1378 vec![],
1379 );
1380 assert_data_tree!(diagnostics_hierarchy, key: {
1381 value1: &*TEST_REGEX,
1382 });
1383 assert_data_tree!(diagnostics_hierarchy, key: {
1384 value1: Regex::new("a{4}b{4}").unwrap(),
1385 });
1386 assert_data_tree!(diagnostics_hierarchy, key: {
1387 value1: Regex::new("a{4}").unwrap(),
1388 });
1389 assert_data_tree!(diagnostics_hierarchy, key: {
1390 value1: Regex::new("a{2}b{2}").unwrap(),
1391 });
1392 assert_data_tree!(diagnostics_hierarchy, key: {
1393 value1: Regex::new("b{4}").unwrap(),
1394 });
1395 }
1396
1397 #[fuchsia::test]
1398 #[should_panic]
1399 async fn test_string_property_regex_no_match() {
1400 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1401 "key",
1402 vec![Property::String("value1".to_string(), "bbbbcccc".to_string())],
1403 vec![],
1404 );
1405 assert_data_tree!(diagnostics_hierarchy, key: {
1406 value: Regex::new("b{2}d{2}").unwrap(),
1407 });
1408 }
1409
1410 #[fuchsia::test]
1411 #[should_panic]
1412 async fn test_string_property_regex_wrong_type() {
1413 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1414 "key",
1415 vec![Property::Int("value1".to_string(), 10i64)],
1416 vec![],
1417 );
1418 assert_data_tree!(diagnostics_hierarchy, key: {
1419 value1: Regex::new("a{4}").unwrap(),
1420 });
1421 }
1422
1423 #[fuchsia::test]
1424 async fn test_any_bytes_property_passes() {
1425 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1426 "key",
1427 vec![
1428 Property::Bytes("value1".to_string(), vec![1, 2, 3]),
1429 Property::Bytes("value2".to_string(), vec![4, 5, 6]),
1430 ],
1431 vec![],
1432 );
1433 assert_data_tree!(diagnostics_hierarchy, key: {
1434 value1: AnyBytesProperty,
1435 value2: AnyBytesProperty,
1436 });
1437 }
1438
1439 #[fuchsia::test]
1440 #[should_panic]
1441 async fn test_any_bytes_property_fails() {
1442 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1443 "key",
1444 vec![Property::Int("value1".to_string(), 10i64)],
1445 vec![],
1446 );
1447 assert_data_tree!(diagnostics_hierarchy, key: {
1448 value1: AnyBytesProperty,
1449 });
1450 }
1451
1452 #[fuchsia::test]
1453 async fn test_nonzero_uint_property_passes() {
1454 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1455 "key",
1456 vec![
1457 Property::Uint("value1".to_string(), 10u64),
1458 Property::Uint("value2".to_string(), 20u64),
1459 ],
1460 vec![],
1461 );
1462 assert_data_tree!(diagnostics_hierarchy, key: {
1463 value1: NonZeroUintProperty,
1464 value2: NonZeroUintProperty,
1465 });
1466 }
1467
1468 #[fuchsia::test]
1469 #[should_panic]
1470 async fn test_nonzero_uint_property_fails_on_zero() {
1471 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1472 "key",
1473 vec![Property::Uint("value1".to_string(), 0u64)],
1474 vec![],
1475 );
1476 assert_data_tree!(diagnostics_hierarchy, key: {
1477 value1: NonZeroUintProperty,
1478 });
1479 }
1480
1481 #[fuchsia::test]
1482 #[should_panic]
1483 async fn test_nonzero_uint_property_fails() {
1484 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1485 "key",
1486 vec![Property::Int("value1".to_string(), 10i64)],
1487 vec![],
1488 );
1489 assert_data_tree!(diagnostics_hierarchy, key: {
1490 value1: NonZeroUintProperty,
1491 });
1492 }
1493
1494 #[fuchsia::test]
1495 async fn test_nonzero_int_property_passes() {
1496 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1497 "key",
1498 vec![Property::Int("value1".to_string(), 10), Property::Int("value2".to_string(), 20)],
1499 vec![],
1500 );
1501 assert_data_tree!(diagnostics_hierarchy, key: {
1502 value1: NonZeroIntProperty,
1503 value2: NonZeroIntProperty,
1504 });
1505 }
1506
1507 #[fuchsia::test]
1508 #[should_panic]
1509 async fn test_nonzero_int_property_fails_on_zero() {
1510 let diagnostics_hierarchy =
1511 DiagnosticsHierarchy::new("key", vec![Property::Int("value1".to_string(), 0)], vec![]);
1512 assert_data_tree!(diagnostics_hierarchy, key: {
1513 value1: NonZeroIntProperty,
1514 });
1515 }
1516
1517 #[fuchsia::test]
1518 #[should_panic]
1519 async fn test_nonzero_int_property_fails() {
1520 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1521 "key",
1522 vec![Property::Uint("value1".to_string(), 10u64)],
1523 vec![],
1524 );
1525 assert_data_tree!(diagnostics_hierarchy, key: {
1526 value1: NonZeroIntProperty,
1527 });
1528 }
1529
1530 #[fuchsia::test]
1531 async fn test_uint_property_passes() {
1532 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1533 "key",
1534 vec![
1535 Property::Uint("value1".to_string(), 10u64),
1536 Property::Uint("value2".to_string(), 20u64),
1537 ],
1538 vec![],
1539 );
1540 assert_data_tree!(diagnostics_hierarchy, key: {
1541 value1: AnyUintProperty,
1542 value2: AnyUintProperty,
1543 });
1544 }
1545
1546 #[fuchsia::test]
1547 #[should_panic]
1548 async fn test_uint_property_fails() {
1549 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1550 "key",
1551 vec![Property::Int("value1".to_string(), 10i64)],
1552 vec![],
1553 );
1554 assert_data_tree!(diagnostics_hierarchy, key: {
1555 value1: AnyUintProperty,
1556 });
1557 }
1558
1559 #[fuchsia::test]
1560 async fn test_int_property_passes() {
1561 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1562 "key",
1563 vec![
1564 Property::Int("value1".to_string(), 10i64),
1565 Property::Int("value2".to_string(), 20i64),
1566 ],
1567 vec![],
1568 );
1569 assert_data_tree!(diagnostics_hierarchy, key: {
1570 value1: AnyIntProperty,
1571 value2: AnyIntProperty,
1572 });
1573 }
1574
1575 #[fuchsia::test]
1576 #[should_panic]
1577 async fn test_int_property_fails() {
1578 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1579 "key",
1580 vec![Property::Uint("value1".to_string(), 0u64)],
1581 vec![],
1582 );
1583 assert_data_tree!(diagnostics_hierarchy, key: {
1584 value1: AnyIntProperty,
1585 });
1586 }
1587
1588 #[fuchsia::test]
1589 async fn test_double_property_passes() {
1590 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1591 "key",
1592 vec![
1593 Property::Double("value1".to_string(), std::f64::consts::PI),
1594 Property::Double("value2".to_string(), std::f64::consts::E),
1595 ],
1596 vec![],
1597 );
1598 assert_data_tree!(diagnostics_hierarchy, key: {
1599 value1: AnyDoubleProperty,
1600 value2: AnyDoubleProperty,
1601 });
1602 }
1603
1604 #[fuchsia::test]
1605 #[should_panic]
1606 async fn test_double_property_fails() {
1607 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1608 "key",
1609 vec![Property::Uint("value1".to_string(), 0u64)],
1610 vec![],
1611 );
1612 assert_data_tree!(diagnostics_hierarchy, key: {
1613 value1: AnyDoubleProperty,
1614 });
1615 }
1616
1617 #[fuchsia::test]
1618 async fn test_numeric_property_passes() {
1619 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1620 "key",
1621 vec![
1622 Property::Int("value1".to_string(), 10i64),
1623 Property::Uint("value2".to_string(), 20u64),
1624 Property::Double("value3".to_string(), std::f64::consts::PI),
1625 ],
1626 vec![],
1627 );
1628 assert_data_tree!(diagnostics_hierarchy, key: {
1629 value1: AnyNumericProperty,
1630 value2: AnyNumericProperty,
1631 value3: AnyNumericProperty,
1632 });
1633 }
1634
1635 #[fuchsia::test]
1636 #[should_panic]
1637 async fn test_numeric_property_fails() {
1638 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1639 "key",
1640 vec![Property::String("value1".to_string(), "a".to_string())],
1641 vec![],
1642 );
1643 assert_data_tree!(diagnostics_hierarchy, key: {
1644 value1: AnyNumericProperty,
1645 });
1646 }
1647
1648 #[fuchsia::test]
1649 async fn test_bool_property_passes() {
1650 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1651 "key",
1652 vec![
1653 Property::Bool("value1".to_string(), true),
1654 Property::Bool("value2".to_string(), false),
1655 ],
1656 vec![],
1657 );
1658 assert_data_tree!(diagnostics_hierarchy, key: {
1659 value1: AnyBoolProperty,
1660 value2: AnyBoolProperty,
1661 });
1662 }
1663
1664 #[fuchsia::test]
1665 #[should_panic]
1666 async fn test_bool_property_fails() {
1667 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1668 "key",
1669 vec![Property::Uint("value1".to_string(), 0u64)],
1670 vec![],
1671 );
1672 assert_data_tree!(diagnostics_hierarchy, key: {
1673 value1: AnyBoolProperty,
1674 });
1675 }
1676
1677 #[fuchsia::test]
1678 async fn test_string_list() {
1679 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1680 "key",
1681 vec![
1682 Property::StringList("value1".to_string(), vec!["a".to_string(), "b".to_string()]),
1683 Property::StringList("value2".to_string(), vec!["c".to_string(), "d".to_string()]),
1684 ],
1685 vec![],
1686 );
1687 assert_data_tree!(diagnostics_hierarchy, key: {
1688 value1: vec!["a", "b"],
1689 value2: vec!["c".to_string(), "d".to_string()],
1690 });
1691 }
1692
1693 #[fuchsia::test]
1694 #[should_panic]
1695 async fn test_string_list_failure() {
1696 let diagnostics_hierarchy = DiagnosticsHierarchy::new(
1697 "key",
1698 vec![Property::StringList(
1699 "value1".to_string(),
1700 vec!["a".to_string(), "b".to_string()],
1701 )],
1702 vec![],
1703 );
1704 assert_data_tree!(diagnostics_hierarchy, key: {
1705 value1: vec![1i64, 2],
1706 });
1707 }
1708
1709 #[test]
1710 fn test_diff_from_text() {
1711 let original = "foo\nbar\nbaz";
1712 let update = "foo\nbaz\nqux";
1713
1714 let changeset = Diff::from_text(original, update);
1715 assert_eq!(
1716 changeset.0.diffs,
1717 vec![
1718 Same("foo".to_string()),
1719 Rem("bar".to_string()),
1720 Same("baz".to_string()),
1721 Add("qux".to_string())
1722 ]
1723 )
1724 }
1725
1726 fn simple_tree() -> DiagnosticsHierarchy {
1727 DiagnosticsHierarchy::new(
1728 "key",
1729 vec![
1730 Property::String("sub".to_string(), "sub_value".to_string()),
1731 Property::String("sub2".to_string(), "sub2_value".to_string()),
1732 ],
1733 vec![],
1734 )
1735 }
1736
1737 fn complex_tree() -> DiagnosticsHierarchy {
1738 DiagnosticsHierarchy::new(
1739 "key",
1740 vec![
1741 Property::String("sub".to_string(), "sub_value".to_string()),
1742 Property::String("sub2".to_string(), "sub2_value".to_string()),
1743 ],
1744 vec![
1745 DiagnosticsHierarchy::new(
1746 "child1",
1747 vec![Property::Int("child1_sub".to_string(), 10i64)],
1748 vec![],
1749 ),
1750 DiagnosticsHierarchy::new(
1751 "child2",
1752 vec![Property::Uint("child2_sub".to_string(), 20u64)],
1753 vec![],
1754 ),
1755 ],
1756 )
1757 }
1758
1759 fn non_unique_prop_tree() -> DiagnosticsHierarchy {
1760 DiagnosticsHierarchy::new(
1761 "key",
1762 vec![
1763 Property::String("prop".to_string(), "prop_value#0".to_string()),
1764 Property::String("prop".to_string(), "prop_value#1".to_string()),
1765 ],
1766 vec![],
1767 )
1768 }
1769
1770 fn non_unique_child_tree() -> DiagnosticsHierarchy {
1771 DiagnosticsHierarchy::new(
1772 "key",
1773 vec![],
1774 vec![
1775 DiagnosticsHierarchy::new(
1776 "child",
1777 vec![Property::Int("prop".to_string(), 10i64)],
1778 vec![],
1779 ),
1780 DiagnosticsHierarchy::new(
1781 "child",
1782 vec![Property::Int("prop".to_string(), 20i64)],
1783 vec![],
1784 ),
1785 ],
1786 )
1787 }
1788}