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