diagnostics_hierarchy/
lib.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Diagnostics hierarchy
6//!
7//! This library provides a tree strcture used to store diagnostics data such as inspect and logs,
8//! as well as utilities for reading from it, serializing and deserializing it and testing it.
9
10use base64::display::Base64Display;
11use fidl_fuchsia_diagnostics::{
12    PropertySelector, Selector, StringSelector, StringSelectorUnknown, SubtreeSelector,
13    TreeSelector,
14};
15use num_derive::{FromPrimitive, ToPrimitive};
16use num_traits::bounds::Bounded;
17use selectors::ValidateExt;
18use serde::{Deserialize, Serialize};
19use std::borrow::{Borrow, Cow};
20use std::cmp::Ordering;
21use std::collections::BTreeMap;
22use std::fmt::{Display, Formatter, Result as FmtResult};
23use std::ops::{Add, AddAssign, MulAssign};
24use thiserror::Error;
25
26pub mod macros;
27pub mod serialization;
28
29/// Format in which the array will be read.
30///
31/// Histograms are formatted with its parameters inline with its buckets:
32///
33/// - ...N parameters...
34/// - underflow_bucket
35/// - ...M buckets...
36/// - overflow_bucket
37///
38/// Helper functions exist to make it easier to work with these formats:
39///
40/// - [`ArrayFormat::underflow_bucket_index`] returns the index of the underflow
41///   bucket, which also happens to be the first bucket.
42/// - [`ArrayFormat::extra_slots`] returns the number of additional slots
43///   necessary to store parameters and underflow buckets.
44///
45#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
46#[repr(u8)]
47pub enum ArrayFormat {
48    /// Regular array, it stores N values in N slots.
49    Default = 0,
50
51    /// The array is a linear histogram with N buckets and N+4 slots, which are:
52    /// - param_floor_value
53    /// - param_step_size
54    /// - underflow_bucket
55    /// - ...N buckets...
56    /// - overflow_bucket
57    LinearHistogram = 1,
58
59    /// The array is an exponential histogram with N buckets and N+5 slots, which are:
60    /// - param_floor_value
61    /// - param_initial_step
62    /// - param_step_multiplier
63    /// - underflow_bucket
64    /// - ...N buckets...
65    /// - overflow_bucket
66    ExponentialHistogram = 2,
67}
68
69impl ArrayFormat {
70    /// Return index of the underflow bucket for histograms, or 0 if not a
71    /// histogram.
72    pub const fn underflow_bucket_index(self) -> usize {
73        match self {
74            ArrayFormat::Default => 0,
75            ArrayFormat::LinearHistogram => 2,
76            ArrayFormat::ExponentialHistogram => 3,
77        }
78    }
79
80    /// Return index of the overflow bucket for histograms, or 0 if not a
81    /// histogram. Depends on the number of buckets.
82    pub const fn overflow_bucket_index(self, buckets: usize) -> usize {
83        match self {
84            ArrayFormat::Default => 0,
85            ArrayFormat::LinearHistogram => self.extra_slots() + buckets - 1,
86            ArrayFormat::ExponentialHistogram => self.extra_slots() + buckets - 1,
87        }
88    }
89
90    /// Return count of extra slots needed for storing parameters and
91    /// underflow/overflow slots.
92    pub const fn extra_slots(self) -> usize {
93        match self {
94            // 0 parameters + 0 extra buckets
95            ArrayFormat::Default => 0,
96            // 2 parameters (floor, step size) + underflow bucket + overflow bucket
97            ArrayFormat::LinearHistogram => 4,
98            // 3 parameters (floor, initial step, step multiplier) + underflow bucket + overflow bucket
99            ArrayFormat::ExponentialHistogram => 5,
100        }
101    }
102}
103
104/// A hierarchy of nodes representing structured data, such as Inspect or
105/// structured log data.
106///
107/// Each hierarchy consists of properties, and a map of named child hierarchies.
108#[derive(Clone, Debug, PartialEq)]
109pub struct DiagnosticsHierarchy<Key = String> {
110    /// The name of this node.
111    pub name: String,
112
113    /// The properties for the node.
114    pub properties: Vec<Property<Key>>,
115
116    /// The children of this node.
117    pub children: Vec<DiagnosticsHierarchy<Key>>,
118
119    /// Values that were impossible to load.
120    pub missing: Vec<MissingValue>,
121}
122
123/// A value that couldn't be loaded in the hierarchy and the reason.
124#[derive(Clone, Debug, PartialEq)]
125pub struct MissingValue {
126    /// Specific reason why the value couldn't be loaded.
127    pub reason: MissingValueReason,
128
129    /// The name of the value.
130    pub name: String,
131}
132
133/// Reasons why the value couldn't be loaded.
134#[derive(Clone, Debug, PartialEq)]
135pub enum MissingValueReason {
136    /// A referenced hierarchy in the link was not found.
137    LinkNotFound,
138
139    /// A linked hierarchy couldn't be parsed.
140    LinkParseFailure,
141
142    /// There was no attempt to read the link.
143    LinkNeverExpanded,
144
145    /// There was a timeout while reading.
146    Timeout,
147}
148
149/// Compares the names of two properties or nodes. If both are unsigned integers, then it compares
150/// their numerical value.
151fn name_partial_cmp(a: &str, b: &str) -> Ordering {
152    match (a.parse::<u64>(), b.parse::<u64>()) {
153        (Ok(n), Ok(m)) => n.partial_cmp(&m).unwrap(),
154        _ => a.partial_cmp(b).unwrap(),
155    }
156}
157
158impl<Key> DiagnosticsHierarchy<Key>
159where
160    Key: AsRef<str>,
161{
162    /// Sorts the properties and children of the diagnostics hierarchy by name.
163    pub fn sort(&mut self) {
164        self.properties.sort_by(|p1, p2| name_partial_cmp(p1.name(), p2.name()));
165        self.children.sort_by(|c1, c2| name_partial_cmp(&c1.name, &c2.name));
166        for child in self.children.iter_mut() {
167            child.sort();
168        }
169    }
170
171    /// Creates a new empty diagnostics hierarchy with the root node named "root".
172    pub fn new_root() -> Self {
173        DiagnosticsHierarchy::new("root", vec![], vec![])
174    }
175
176    /// Creates a new diagnostics hierarchy with the given `name` for the root and the given
177    /// `properties` and `children` under that root.
178    pub fn new(
179        name: impl Into<String>,
180        properties: Vec<Property<Key>>,
181        children: Vec<DiagnosticsHierarchy<Key>>,
182    ) -> Self {
183        Self { name: name.into(), properties, children, missing: vec![] }
184    }
185
186    /// Either returns an existing child of `self` with name `name` or creates
187    /// a new child with name `name`.
188    pub fn get_or_add_child_mut<T>(&mut self, name: T) -> &mut DiagnosticsHierarchy<Key>
189    where
190        T: AsRef<str>,
191    {
192        // We have to use indices to iterate here because the borrow checker cannot
193        // deduce that there are no borrowed values in the else-branch.
194        // TODO(https://fxbug.dev/42122598): We could make this cleaner by changing the DiagnosticsHierarchy
195        // children to hashmaps.
196        match (0..self.children.len()).find(|&i| self.children[i].name == name.as_ref()) {
197            Some(matching_index) => &mut self.children[matching_index],
198            None => {
199                self.children.push(DiagnosticsHierarchy::new(name.as_ref(), vec![], vec![]));
200                self.children
201                    .last_mut()
202                    .expect("We just added an entry so we cannot get None here.")
203            }
204        }
205    }
206
207    /// Add a child to this DiagnosticsHierarchy.
208    ///
209    /// Note: It is possible to create multiple children with the same name using this method, but
210    /// readers may not support such a case.
211    pub fn add_child(&mut self, insert: DiagnosticsHierarchy<Key>) {
212        self.children.push(insert);
213    }
214
215    /// Creates and returns a new Node whose location in a hierarchy
216    /// rooted at `self` is defined by node_path.
217    ///
218    /// Requires: that node_path is not empty.
219    /// Requires: that node_path begin with the key fragment equal to the name of the node
220    ///           that add is being called on.
221    ///
222    /// NOTE: Inspect VMOs may allow multiple nodes of the same name. In this case,
223    ///        the first node found is returned.
224    pub fn get_or_add_node<T>(&mut self, node_path: &[T]) -> &mut DiagnosticsHierarchy<Key>
225    where
226        T: AsRef<str>,
227    {
228        assert!(!node_path.is_empty());
229        let mut iter = node_path.iter();
230        let first_path_string = iter.next().unwrap().as_ref();
231        // It is an invariant that the node path start with the key fragment equal to the
232        // name of the node that get_or_add_node is called on.
233        assert_eq!(first_path_string, &self.name);
234        let mut curr_node = self;
235        for node_path_entry in iter {
236            curr_node = curr_node.get_or_add_child_mut(node_path_entry);
237        }
238        curr_node
239    }
240
241    /// Inserts a new Property into this hierarchy.
242    pub fn add_property(&mut self, property: Property<Key>) {
243        self.properties.push(property);
244    }
245
246    /// Inserts a new Property into a Node whose location in a hierarchy
247    /// rooted at `self` is defined by node_path.
248    ///
249    /// Requires: that node_path is not empty.
250    /// Requires: that node_path begin with the key fragment equal to the name of the node
251    ///           that add is being called on.
252    ///
253    /// NOTE: Inspect VMOs may allow multiple nodes of the same name. In this case,
254    ///       the property is added to the first node found.
255    pub fn add_property_at_path<T>(&mut self, node_path: &[T], property: Property<Key>)
256    where
257        T: AsRef<str>,
258    {
259        self.get_or_add_node(node_path).properties.push(property);
260    }
261
262    /// Provides an iterator over the diagnostics hierarchy returning properties in pre-order.
263    pub fn property_iter(&self) -> DiagnosticsHierarchyIterator<'_, Key, PropertyIter> {
264        DiagnosticsHierarchyIterator::new(self)
265    }
266
267    pub fn error_iter(&self) -> DiagnosticsHierarchyIterator<'_, Key, ErrorIter> {
268        DiagnosticsHierarchyIterator::new(self)
269    }
270
271    /// Adds a value that couldn't be read. This can happen when loading a lazy child.
272    pub fn add_missing(&mut self, reason: MissingValueReason, name: String) {
273        self.missing.push(MissingValue { reason, name });
274    }
275    /// Returns the property of the given |name| if one exists.
276    pub fn get_property(&self, name: &str) -> Option<&Property<Key>> {
277        self.properties.iter().find(|prop| prop.name() == name)
278    }
279
280    /// Returns the child of the given |name| if one exists.
281    pub fn get_child(&self, name: &str) -> Option<&DiagnosticsHierarchy<Key>> {
282        self.children.iter().find(|node| node.name == name)
283    }
284
285    /// Returns a mutable reference to the child of the given |name| if one exists.
286    pub fn get_child_mut(&mut self, name: &str) -> Option<&mut DiagnosticsHierarchy<Key>> {
287        self.children.iter_mut().find(|node| node.name == name)
288    }
289
290    /// Returns the child of the given |path| if one exists.
291    pub fn get_child_by_path(&self, path: &[&str]) -> Option<&DiagnosticsHierarchy<Key>> {
292        let mut result = Some(self);
293        for name in path {
294            result = result.and_then(|node| node.get_child(name));
295        }
296        result
297    }
298
299    /// Returns a mutable reference to the child of the given |path| if one exists.
300    pub fn get_child_by_path_mut(
301        &mut self,
302        path: &[&str],
303    ) -> Option<&mut DiagnosticsHierarchy<Key>> {
304        let mut result = Some(self);
305        for name in path {
306            result = result.and_then(|node| node.get_child_mut(name));
307        }
308        result
309    }
310
311    /// Returns the property of the given |name| if one exists.
312    pub fn get_property_by_path(&self, path: &[&str]) -> Option<&Property<Key>> {
313        let node = self.get_child_by_path(&path[..path.len() - 1]);
314        node.and_then(|node| node.get_property(path[path.len() - 1]))
315    }
316}
317
318impl<Key> DiagnosticsHierarchy<Key>
319where
320    Key: PartialEq,
321{
322    /// Recursively merge another [`DiagnosticsHierarchy`] into this one.
323    pub fn merge(&mut self, other: DiagnosticsHierarchy<Key>) {
324        for other_property in other.properties {
325            if let Some(existing) =
326                self.properties.iter_mut().find(|p| p.key() == other_property.key())
327            {
328                *existing = other_property;
329            } else {
330                self.properties.push(other_property);
331            }
332        }
333
334        for other_child in other.children {
335            if let Some(existing_child) =
336                self.children.iter_mut().find(|c| c.name == other_child.name)
337            {
338                existing_child.merge(other_child);
339            } else {
340                // Remove missing errors for matching lazy nodes.
341                self.missing.retain(|m| m.name != other_child.name);
342
343                self.children.push(other_child);
344            }
345        }
346
347        for other_missing in other.missing {
348            if !self.missing.contains(&other_missing) {
349                self.missing.push(other_missing);
350            }
351        }
352    }
353}
354
355macro_rules! property_type_getters_ref {
356    ($([$variant:ident, $fn_name:ident, $type:ty]),*) => {
357        paste::item! {
358          impl<Key> Property<Key> {
359              $(
360                  #[doc = "Returns the " $variant " value or `None` if the property isn't of that type"]
361                  pub fn $fn_name(&self) -> Option<&$type> {
362                      match self {
363                          Property::$variant(_, value) => Some(value),
364                          _ => None,
365                      }
366                  }
367              )*
368          }
369        }
370    }
371}
372
373macro_rules! property_type_getters_copy {
374    ($([$variant:ident, $fn_name:ident, $type:ty]),*) => {
375        paste::item! {
376          impl<Key> Property<Key> {
377              $(
378                  #[doc = "Returns the " $variant " value or `None` if the property isn't of that type"]
379                  pub fn $fn_name(&self) -> Option<$type> {
380                      match self {
381                          Property::$variant(_, value) => Some(*value),
382                          _ => None,
383                      }
384                  }
385              )*
386          }
387        }
388    }
389}
390
391property_type_getters_copy!(
392    [Int, int, i64],
393    [Uint, uint, u64],
394    [Double, double, f64],
395    [Bool, boolean, bool]
396);
397
398property_type_getters_ref!(
399    [String, string, str],
400    [Bytes, bytes, [u8]],
401    [DoubleArray, double_array, ArrayContent<f64>],
402    [IntArray, int_array, ArrayContent<i64>],
403    [UintArray, uint_array, ArrayContent<u64>],
404    [StringList, string_list, [String]]
405);
406
407struct WorkStackEntry<'a, Key> {
408    node: &'a DiagnosticsHierarchy<Key>,
409    key: Vec<&'a str>,
410}
411
412pub struct PropertyIter;
413pub struct ErrorIter;
414
415pub struct DiagnosticsHierarchyIterator<'a, Key, PropOrIterMarker> {
416    work_stack: Vec<WorkStackEntry<'a, Key>>,
417    current_key: Vec<&'a str>,
418    current_node: Option<&'a DiagnosticsHierarchy<Key>>,
419    current_index: usize,
420    phantom: std::marker::PhantomData<PropOrIterMarker>,
421}
422
423enum EndOfTheLine<'a, T, Key> {
424    Yes(Option<T>),
425    No(&'a DiagnosticsHierarchy<Key>),
426}
427
428impl<'a, Key, Marker> DiagnosticsHierarchyIterator<'a, Key, Marker> {
429    /// Creates a new iterator for the given `hierarchy`.
430    fn new(hierarchy: &'a DiagnosticsHierarchy<Key>) -> Self {
431        DiagnosticsHierarchyIterator {
432            work_stack: vec![WorkStackEntry { node: hierarchy, key: vec![&hierarchy.name] }],
433            current_key: vec![],
434            current_node: None,
435            current_index: 0,
436            phantom: std::marker::PhantomData,
437        }
438    }
439
440    /// Get the next node. This abstracts stack management away from the type being iterated over.
441    fn get_node<T, U: 'a, F: FnOnce(&'a DiagnosticsHierarchy<Key>) -> &Vec<U>>(
442        &mut self,
443        iterable_node_data: F,
444    ) -> EndOfTheLine<'a, (Vec<&'a str>, Option<&'a T>), Key> {
445        match self.current_node {
446            // If we are going through a node's data, that node will be set here.
447            Some(node) => EndOfTheLine::No(node),
448            None => {
449                // If we don't have a node we are currently working with, then go to the next
450                // node in our stack.
451                let Some(WorkStackEntry { node, key }) = self.work_stack.pop() else {
452                    return EndOfTheLine::Yes(None);
453                };
454
455                // Push to the stack all children of the new node.
456                for child in node.children.iter() {
457                    let mut child_key = key.clone();
458                    child_key.push(&child.name);
459                    self.work_stack.push(WorkStackEntry { node: child, key: child_key })
460                }
461
462                // If this node doesn't have any data we care about, we still want to return that it
463                // exists, so we return with a None for data type we are examining.
464                if iterable_node_data(node).is_empty() {
465                    return EndOfTheLine::Yes(Some((key.clone(), None)));
466                }
467
468                self.current_index = 0;
469                self.current_key = key;
470
471                EndOfTheLine::No(node)
472            }
473        }
474    }
475
476    fn advance_index<T>(
477        &mut self,
478        data: &'a [T],
479        new_current: &'a DiagnosticsHierarchy<Key>,
480    ) -> &'a T {
481        let datum = &data[self.current_index];
482        self.current_index += 1;
483        self.current_node = Some(new_current);
484        datum
485    }
486}
487
488impl<'a, Key> Iterator for DiagnosticsHierarchyIterator<'a, Key, PropertyIter> {
489    /// Each item is a path to the node holding the resulting property.
490    /// If a node has no properties, a `None` will be returned for it.
491    /// If a node has properties a `Some` will be returned for each property and no `None` will be
492    /// returned.
493    type Item = (Vec<&'a str>, Option<&'a Property<Key>>);
494
495    fn next(&mut self) -> Option<Self::Item> {
496        loop {
497            let node = match self.get_node(|node| &node.properties) {
498                EndOfTheLine::Yes(r) => return r,
499                EndOfTheLine::No(n) => n,
500            };
501
502            // We were already done with this node. Try the next item in our stack.
503            if self.current_index == node.properties.len() {
504                self.current_node = None;
505                continue;
506            }
507
508            // Return the current property and advance our index to the next node we want to
509            // explore.
510            let property = self.advance_index(&node.properties, node);
511
512            return Some((self.current_key.clone(), Some(property)));
513        }
514    }
515}
516
517impl<'a, Key> Iterator for DiagnosticsHierarchyIterator<'a, Key, ErrorIter> {
518    /// Each item is a path to the node with a missing link.
519    /// If a node has no missing links, a `None` will be returned for it.
520    /// If a node has missing links a `Some` will be returned for each link and no `None` will be
521    /// returned.
522    type Item = (Vec<&'a str>, Option<&'a MissingValue>);
523
524    fn next(&mut self) -> Option<Self::Item> {
525        loop {
526            let node = match self.get_node(|node| &node.missing) {
527                EndOfTheLine::Yes(r) => return r,
528                EndOfTheLine::No(n) => n,
529            };
530
531            // We were already done with this node. Try the next item in our stack.
532            if self.current_index == node.missing.len() {
533                self.current_node = None;
534                continue;
535            }
536
537            // Return the current error and advance our index to the next node we want to
538            // explore.
539            let err = self.advance_index(&node.missing, node);
540            return Some((self.current_key.clone(), Some(err)));
541        }
542    }
543}
544
545/// A named property. Each of the fields consists of (name, value).
546///
547/// Key is the type of the property's name and is typically a string. In cases where
548/// there are well known, common property names, an alternative may be used to
549/// reduce copies of the name.
550#[derive(Debug, PartialEq, Clone)]
551pub enum Property<Key = String> {
552    /// The value is a string.
553    String(Key, String),
554
555    /// The value is a bytes vector.
556    Bytes(Key, Vec<u8>),
557
558    /// The value is an integer.
559    Int(Key, i64),
560
561    /// The value is an unsigned integer.
562    Uint(Key, u64),
563
564    /// The value is a double.
565    Double(Key, f64),
566
567    /// The value is a boolean.
568    Bool(Key, bool),
569
570    /// The value is a double array.
571    DoubleArray(Key, ArrayContent<f64>),
572
573    /// The value is an integer array.
574    IntArray(Key, ArrayContent<i64>),
575
576    /// The value is an unsigned integer array.
577    UintArray(Key, ArrayContent<u64>),
578
579    /// The value is a list of strings.
580    StringList(Key, Vec<String>),
581}
582
583impl<K> Property<K> {
584    /// Returns the key of a property
585    pub fn key(&self) -> &K {
586        match self {
587            Property::String(k, _) => k,
588            Property::Bytes(k, _) => k,
589            Property::Int(k, _) => k,
590            Property::Uint(k, _) => k,
591            Property::Double(k, _) => k,
592            Property::Bool(k, _) => k,
593            Property::DoubleArray(k, _) => k,
594            Property::IntArray(k, _) => k,
595            Property::UintArray(k, _) => k,
596            Property::StringList(k, _) => k,
597        }
598    }
599
600    /// Returns a string indicating which variant of property this is, useful for printing
601    /// debug values.
602    pub fn discriminant_name(&self) -> &'static str {
603        match self {
604            Property::String(_, _) => "String",
605            Property::Bytes(_, _) => "Bytes",
606            Property::Int(_, _) => "Int",
607            Property::IntArray(_, _) => "IntArray",
608            Property::Uint(_, _) => "Uint",
609            Property::UintArray(_, _) => "UintArray",
610            Property::Double(_, _) => "Double",
611            Property::DoubleArray(_, _) => "DoubleArray",
612            Property::Bool(_, _) => "Bool",
613            Property::StringList(_, _) => "StringList",
614        }
615    }
616
617    /// Return a a numeric property as a signed integer. Useful for having a single function to call
618    /// when a property has been passed through JSON, potentially losing its original signedness.
619    ///
620    /// Note: unsigned integers larger than `isize::MAX` will be returned as `None`. If you expect
621    /// values that high, consider calling `Property::int()` and `Property::uint()` directly.
622    pub fn number_as_int(&self) -> Option<i64> {
623        match self {
624            Property::Int(_, i) => Some(*i),
625            Property::Uint(_, u) => i64::try_from(*u).ok(),
626            Property::String(..)
627            | Property::Bytes(..)
628            | Property::Double(..)
629            | Property::Bool(..)
630            | Property::DoubleArray(..)
631            | Property::IntArray(..)
632            | Property::UintArray(..)
633            | Property::StringList(..) => None,
634        }
635    }
636}
637
638impl<K> Display for Property<K>
639where
640    K: AsRef<str>,
641{
642    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
643        macro_rules! pair {
644            ($fmt:literal, $val:expr) => {
645                write!(f, "{}={}", self.key().as_ref(), format_args!($fmt, $val))
646            };
647        }
648        match self {
649            Property::String(_, v) => pair!("{}", v),
650            Property::Bytes(_, v) => {
651                pair!("b64:{}", Base64Display::new(v, &base64::engine::general_purpose::STANDARD))
652            }
653            Property::Int(_, v) => pair!("{}", v),
654            Property::Uint(_, v) => pair!("{}", v),
655            Property::Double(_, v) => pair!("{}", v),
656            Property::Bool(_, v) => pair!("{}", v),
657            Property::DoubleArray(_, v) => pair!("{:?}", v),
658            Property::IntArray(_, v) => pair!("{:?}", v),
659            Property::UintArray(_, v) => pair!("{:?}", v),
660            Property::StringList(_, v) => pair!("{:?}", v),
661        }
662    }
663}
664
665/// Errors that can happen in this library.
666#[derive(Debug, Error)]
667pub enum Error {
668    #[error("Missing elements for {histogram_type:?} histogram. Expected {expected}, got {actual}")]
669    MissingHistogramElements { histogram_type: ArrayFormat, expected: usize, actual: usize },
670
671    #[error("TreeSelector only supports property and subtree selection.")]
672    InvalidTreeSelector,
673
674    #[error(transparent)]
675    Selectors(#[from] selectors::Error),
676
677    #[error(transparent)]
678    InvalidSelector(#[from] selectors::ValidationError),
679}
680
681impl Error {
682    fn missing_histogram_elements(histogram_type: ArrayFormat, actual: usize) -> Self {
683        Self::MissingHistogramElements {
684            histogram_type,
685            actual,
686            expected: histogram_type.extra_slots() + 1,
687        }
688    }
689}
690
691/// A linear histogram property.
692#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
693#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
694pub struct LinearHistogram<T> {
695    /// The number of buckets. If indexes is None this should equal counts.len().
696    pub size: usize,
697
698    /// The floor of the lowest bucket (not counting the negative-infinity bucket).
699    pub floor: T,
700
701    /// The increment for each bucket range.
702    pub step: T,
703
704    /// The number of items in each bucket.
705    pub counts: Vec<T>,
706
707    /// If Some<_>, the indexes of nonzero counts.
708    #[serde(skip_serializing_if = "Option::is_none")]
709    pub indexes: Option<Vec<usize>>,
710}
711
712/// An exponential histogram property.
713#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
714#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
715pub struct ExponentialHistogram<T> {
716    /// The number of buckets. If indexes is None this should equal counts.len().
717    pub size: usize,
718
719    /// The floor of the lowest bucket (not counting the negative-infinity bucket).
720    pub floor: T,
721
722    /// The increment for the second floor.
723    pub initial_step: T,
724
725    /// The multiplier for each successive floor.
726    pub step_multiplier: T,
727
728    /// The number of items in each bucket.
729    pub counts: Vec<T>,
730
731    /// If Some<_>, the indexes of nonzero counts.
732    #[serde(skip_serializing_if = "Option::is_none")]
733    pub indexes: Option<Vec<usize>>,
734}
735
736/// Represents the content of a DiagnosticsHierarchy array property: a regular array or a
737/// linear/exponential histogram.
738#[derive(Debug, PartialEq, Clone)]
739pub enum ArrayContent<T> {
740    /// The contents of an array.
741    Values(Vec<T>),
742
743    /// The data for a linear histogram.
744    LinearHistogram(LinearHistogram<T>),
745
746    // The data for an exponential histogram.
747    ExponentialHistogram(ExponentialHistogram<T>),
748}
749
750impl<T> ArrayContent<T>
751where
752    T: Add<Output = T> + num_traits::Zero + AddAssign + Copy + MulAssign + PartialEq + Bounded,
753{
754    /// Creates a new ArrayContent parsing the `values` based on the given `format`.
755    pub fn new(values: Vec<T>, format: ArrayFormat) -> Result<Self, Error> {
756        let (counts, indexes) = match format {
757            ArrayFormat::Default => return Ok(Self::Values(values)),
758            ArrayFormat::LinearHistogram | ArrayFormat::ExponentialHistogram => {
759                // Check that the minimum required values are available:
760                // floor, step size, underflow, bucket 0, overflow
761                if values.len() < format.extra_slots() + 1 {
762                    return Err(Error::missing_histogram_elements(format, values.len()));
763                }
764                let buckets = &values[format.underflow_bucket_index()..];
765
766                match serialization::maybe_condense_histogram(buckets, &None) {
767                    None => (buckets.to_vec(), None),
768                    Some((counts, indexes)) => (counts, Some(indexes)),
769                }
770            }
771        };
772
773        Ok(match format {
774            ArrayFormat::Default => unreachable!(),
775            ArrayFormat::LinearHistogram => Self::LinearHistogram(LinearHistogram {
776                floor: values[0],
777                step: values[1],
778                counts,
779                indexes,
780                size: values.len() - 2,
781            }),
782            ArrayFormat::ExponentialHistogram => Self::ExponentialHistogram(ExponentialHistogram {
783                floor: values[0],
784                initial_step: values[1],
785                step_multiplier: values[2],
786                counts,
787                indexes,
788                size: values.len() - 3,
789            }),
790        })
791    }
792
793    /// Returns the number of items in the array.
794    pub fn len(&self) -> usize {
795        match self {
796            Self::Values(vals) => vals.len(),
797            Self::LinearHistogram(LinearHistogram { size, .. })
798            | Self::ExponentialHistogram(ExponentialHistogram { size, .. }) => *size,
799        }
800    }
801
802    /// Returns whether the array is empty or not.
803    pub fn is_empty(&self) -> bool {
804        self.len() == 0
805    }
806
807    /// Returns the raw values of this Array content. In the case of a histogram, returns the
808    /// bucket counts.
809    pub fn raw_values(&self) -> Cow<'_, Vec<T>> {
810        match self {
811            Self::Values(values) => Cow::Borrowed(values),
812            Self::LinearHistogram(LinearHistogram { size, counts, indexes, .. })
813            | Self::ExponentialHistogram(ExponentialHistogram { size, counts, indexes, .. }) => {
814                if let Some(indexes) = indexes {
815                    let mut values = vec![T::zero(); *size];
816                    for (count, index) in counts.iter().zip(indexes.iter()) {
817                        if index <= size {
818                            values[*index] = *count;
819                        }
820                    }
821                    Cow::Owned(values)
822                } else {
823                    Cow::Borrowed(counts)
824                }
825            }
826        }
827    }
828}
829
830pub mod testing {
831    use crate::ArrayContent;
832    use num_traits::bounds::Bounded;
833    use std::ops::{Add, AddAssign, MulAssign};
834
835    // Require test code to import CondensableOnDemand to access the
836    // condense_histogram() associated function.
837    pub trait CondensableOnDemand {
838        fn condense_histogram(&mut self);
839    }
840
841    fn condense_counts<T: num_traits::Zero + Copy + PartialEq>(
842        counts: &[T],
843    ) -> (Vec<T>, Vec<usize>) {
844        let mut condensed_counts = vec![];
845        let mut indexes = vec![];
846        for (index, count) in counts.iter().enumerate() {
847            if *count != T::zero() {
848                condensed_counts.push(*count);
849                indexes.push(index);
850            }
851        }
852        (condensed_counts, indexes)
853    }
854
855    impl<T> CondensableOnDemand for ArrayContent<T>
856    where
857        T: Add<Output = T> + num_traits::Zero + AddAssign + Copy + MulAssign + PartialEq + Bounded,
858    {
859        fn condense_histogram(&mut self) {
860            match self {
861                Self::Values(_) => (),
862                Self::LinearHistogram(histogram) => {
863                    if histogram.indexes.is_some() {
864                        return;
865                    }
866                    let (counts, indexes) = condense_counts(&histogram.counts);
867                    histogram.counts = counts;
868                    histogram.indexes = Some(indexes);
869                }
870                Self::ExponentialHistogram(histogram) => {
871                    if histogram.indexes.is_some() {
872                        return;
873                    }
874                    let (counts, indexes) = condense_counts(&histogram.counts);
875                    histogram.counts = counts;
876                    histogram.indexes = Some(indexes);
877                }
878            }
879        }
880    }
881}
882
883impl<Key> Property<Key>
884where
885    Key: AsRef<str>,
886{
887    /// Returns the key of a property.
888    pub fn name(&self) -> &str {
889        match self {
890            Property::String(name, _)
891            | Property::Bytes(name, _)
892            | Property::Int(name, _)
893            | Property::IntArray(name, _)
894            | Property::Uint(name, _)
895            | Property::UintArray(name, _)
896            | Property::Double(name, _)
897            | Property::Bool(name, _)
898            | Property::DoubleArray(name, _)
899            | Property::StringList(name, _) => name.as_ref(),
900        }
901    }
902}
903
904impl<T: Borrow<Selector>> TryFrom<&[T]> for HierarchyMatcher {
905    type Error = Error;
906
907    fn try_from(selectors: &[T]) -> Result<Self, Self::Error> {
908        // TODO(https://fxbug.dev/42069126: remove cloning, the archivist can probably hold
909        // HierarchyMatcher<'static>
910        let mut matcher = HierarchyMatcher::default();
911        for selector in selectors {
912            let selector = selector.borrow();
913            selector.validate().map_err(|e| Error::Selectors(e.into()))?;
914
915            // Safe to unwrap since we already validated the selector.
916            // TODO(https://fxbug.dev/42069126): instead of doing this over Borrow<Selector> do it over
917            // Selector.
918            match selector.tree_selector.clone().unwrap() {
919                TreeSelector::SubtreeSelector(subtree_selector) => {
920                    matcher.insert_subtree(subtree_selector.clone());
921                }
922                TreeSelector::PropertySelector(property_selector) => {
923                    matcher.insert_property(property_selector.clone());
924                }
925                _ => return Err(Error::Selectors(selectors::Error::InvalidTreeSelector)),
926            }
927        }
928        Ok(matcher)
929    }
930}
931
932impl<T: Borrow<Selector>> TryFrom<Vec<T>> for HierarchyMatcher {
933    type Error = Error;
934
935    fn try_from(selectors: Vec<T>) -> Result<Self, Self::Error> {
936        selectors[..].try_into()
937    }
938}
939
940#[derive(Debug)]
941struct OrdStringSelector(StringSelector);
942
943impl From<StringSelector> for OrdStringSelector {
944    fn from(selector: StringSelector) -> Self {
945        Self(selector)
946    }
947}
948
949impl Ord for OrdStringSelector {
950    fn cmp(&self, other: &OrdStringSelector) -> Ordering {
951        match (&self.0, &other.0) {
952            (StringSelector::ExactMatch(s), StringSelector::ExactMatch(o)) => s.cmp(o),
953            (StringSelector::StringPattern(s), StringSelector::StringPattern(o)) => s.cmp(o),
954            (StringSelector::ExactMatch(_), StringSelector::StringPattern(_)) => Ordering::Less,
955            (StringSelector::StringPattern(_), StringSelector::ExactMatch(_)) => Ordering::Greater,
956            (StringSelectorUnknown!(), StringSelector::ExactMatch(_)) => Ordering::Less,
957            (StringSelectorUnknown!(), StringSelector::StringPattern(_)) => Ordering::Less,
958            (StringSelectorUnknown!(), StringSelectorUnknown!()) => Ordering::Equal,
959        }
960    }
961}
962
963impl PartialOrd for OrdStringSelector {
964    fn partial_cmp(&self, other: &OrdStringSelector) -> Option<Ordering> {
965        Some(self.cmp(other))
966    }
967}
968
969impl PartialEq for OrdStringSelector {
970    fn eq(&self, other: &OrdStringSelector) -> bool {
971        match (&self.0, &other.0) {
972            (StringSelector::ExactMatch(s), StringSelector::ExactMatch(o)) => s.eq(o),
973            (StringSelector::StringPattern(s), StringSelector::StringPattern(o)) => s.eq(o),
974            (StringSelector::ExactMatch(_), StringSelector::StringPattern(_)) => false,
975            (StringSelector::StringPattern(_), StringSelector::ExactMatch(_)) => false,
976            (StringSelectorUnknown!(), StringSelectorUnknown!()) => true,
977        }
978    }
979}
980
981impl Eq for OrdStringSelector {}
982
983#[derive(Default, Debug)]
984pub struct HierarchyMatcher {
985    nodes: BTreeMap<OrdStringSelector, HierarchyMatcher>,
986    properties: Vec<OrdStringSelector>,
987    subtree: bool,
988}
989
990impl HierarchyMatcher {
991    pub fn new<I>(selectors: I) -> Result<Self, Error>
992    where
993        I: Iterator<Item = Selector>,
994    {
995        let mut matcher = HierarchyMatcher::default();
996        for selector in selectors {
997            selector.validate().map_err(|e| Error::Selectors(e.into()))?;
998
999            // Safe to unwrap since we already validated the selector.
1000            match selector.tree_selector.unwrap() {
1001                TreeSelector::SubtreeSelector(subtree_selector) => {
1002                    matcher.insert_subtree(subtree_selector);
1003                }
1004                TreeSelector::PropertySelector(property_selector) => {
1005                    matcher.insert_property(property_selector);
1006                }
1007                _ => return Err(Error::Selectors(selectors::Error::InvalidTreeSelector)),
1008            }
1009        }
1010        Ok(matcher)
1011    }
1012
1013    fn insert_subtree(&mut self, selector: SubtreeSelector) {
1014        self.insert(selector.node_path, None);
1015    }
1016
1017    fn insert_property(&mut self, selector: PropertySelector) {
1018        self.insert(selector.node_path, Some(selector.target_properties));
1019    }
1020
1021    fn insert(&mut self, node_path: Vec<StringSelector>, property: Option<StringSelector>) {
1022        // Note: this could have additional optimization so that branches are collapsed into a
1023        // single one (for example foo/bar is included by f*o/bar), however, in practice, we don't
1024        // hit that edge case.
1025        let mut matcher = self;
1026        for node in node_path {
1027            matcher = matcher.nodes.entry(node.into()).or_default();
1028        }
1029        match property {
1030            Some(property) => {
1031                matcher.properties.push(property.into());
1032            }
1033            None => matcher.subtree = true,
1034        }
1035    }
1036}
1037
1038#[derive(Debug, PartialEq)]
1039pub enum SelectResult<'a, Key: Clone> {
1040    Properties(Vec<Cow<'a, Property<Key>>>),
1041    Nodes(Vec<Cow<'a, DiagnosticsHierarchy<Key>>>),
1042}
1043
1044impl<'a, Key: Clone> SelectResult<'a, Key> {
1045    pub fn into_owned(self) -> SelectResult<'static, Key> {
1046        match self {
1047            Self::Properties(v) => SelectResult::Properties(
1048                v.into_iter().map(|v| Cow::Owned(v.into_owned())).collect(),
1049            ),
1050            Self::Nodes(v) => {
1051                SelectResult::Nodes(v.into_iter().map(|v| Cow::Owned(v.into_owned())).collect())
1052            }
1053        }
1054    }
1055
1056    /// Returns Err(()) if `self` is `Self::Nodes`. Otherwise, adds to property list.
1057    fn add_property(&mut self, prop: &'a Property<Key>) {
1058        let Self::Properties(v) = self else {
1059            panic!("must be Self::Properties to call add_property");
1060        };
1061        v.push(Cow::Borrowed(prop));
1062    }
1063
1064    /// Returns Err(()) if `self` is `Self::Properties`. Otherwise, adds to property list.
1065    fn add_node(&mut self, node: &'a DiagnosticsHierarchy<Key>) {
1066        let Self::Nodes(v) = self else {
1067            panic!("must be Self::Nodes to call add_node");
1068        };
1069        v.push(Cow::Borrowed(node));
1070    }
1071}
1072
1073pub fn select_from_hierarchy<'a, 'b, Key>(
1074    root_node: &'a DiagnosticsHierarchy<Key>,
1075    selector: &'b Selector,
1076) -> Result<SelectResult<'a, Key>, Error>
1077where
1078    Key: AsRef<str> + Clone,
1079    'a: 'b,
1080{
1081    selector.validate()?;
1082
1083    struct StackEntry<'a, Key> {
1084        node: &'a DiagnosticsHierarchy<Key>,
1085        node_path_index: usize,
1086        explored_path: Vec<&'a str>,
1087    }
1088
1089    // Safe to unwrap since we validated above.
1090    let (node_path, property_selector, stack_entry) = match selector.tree_selector.as_ref().unwrap()
1091    {
1092        TreeSelector::SubtreeSelector(ref subtree_selector) => (
1093            &subtree_selector.node_path,
1094            None,
1095            StackEntry { node: root_node, node_path_index: 0, explored_path: vec![] },
1096        ),
1097        TreeSelector::PropertySelector(ref property_selector) => (
1098            &property_selector.node_path,
1099            Some(&property_selector.target_properties),
1100            StackEntry { node: root_node, node_path_index: 0, explored_path: vec![] },
1101        ),
1102        _ => return Err(Error::InvalidTreeSelector),
1103    };
1104
1105    let mut stack = vec![stack_entry];
1106    let mut result = if property_selector.is_some() {
1107        SelectResult::Properties(vec![])
1108    } else {
1109        SelectResult::Nodes(vec![])
1110    };
1111
1112    while let Some(StackEntry { node, node_path_index, mut explored_path }) = stack.pop() {
1113        // Unwrap is safe since we validate is_empty right above.
1114        if !selectors::match_string(&node_path[node_path_index], &node.name) {
1115            continue;
1116        }
1117        explored_path.push(&node.name);
1118
1119        // If we are at the last node in the path, then we just need to explore the properties.
1120        // Otherwise, we explore the children of the current node and the properties.
1121        if node_path_index != node_path.len() - 1 {
1122            // If this node matches the next selector we are looking at, then explore its children.
1123            for child in node.children.iter() {
1124                stack.push(StackEntry {
1125                    node: child,
1126                    node_path_index: node_path_index + 1,
1127                    explored_path: explored_path.clone(),
1128                });
1129            }
1130        } else if let Some(s) = property_selector {
1131            // If we have a property selector, then add any properties matching it to our result.
1132            for property in &node.properties {
1133                if selectors::match_string(s, property.key()) {
1134                    result.add_property(property);
1135                }
1136            }
1137        } else {
1138            // If we don't have a property selector and we reached the end of the node path, then
1139            // we should add the current node to the result.
1140            result.add_node(node);
1141        }
1142    }
1143
1144    Ok(result)
1145}
1146
1147/// Filters a hierarchy given a tree selector.
1148pub fn filter_tree<'a, Key, I: IntoIterator<Item = &'a TreeSelector>>(
1149    root_node: DiagnosticsHierarchy<Key>,
1150    selectors: I,
1151) -> Option<DiagnosticsHierarchy<Key>>
1152where
1153    Key: AsRef<str>,
1154{
1155    let mut matcher = HierarchyMatcher::default();
1156    for selector in selectors.into_iter() {
1157        match selector {
1158            TreeSelector::SubtreeSelector(subtree_selector) => {
1159                matcher.insert_subtree(subtree_selector.clone());
1160            }
1161            TreeSelector::PropertySelector(property_selector) => {
1162                matcher.insert_property(property_selector.clone());
1163            }
1164            _ => {}
1165        }
1166    }
1167    filter_hierarchy(root_node, &matcher)
1168}
1169
1170/// Filters a diagnostics hierarchy using a set of path selectors and their associated property
1171/// selectors.
1172///
1173/// If the return type is None that implies that the filter encountered no errors AND the tree was
1174/// filtered to be empty at the end.
1175pub fn filter_hierarchy<Key>(
1176    mut root_node: DiagnosticsHierarchy<Key>,
1177    hierarchy_matcher: &HierarchyMatcher,
1178) -> Option<DiagnosticsHierarchy<Key>>
1179where
1180    Key: AsRef<str>,
1181{
1182    let starts_empty = root_node.children.is_empty() && root_node.properties.is_empty();
1183    if filter_hierarchy_helper(&mut root_node, &[hierarchy_matcher]) {
1184        if !starts_empty && root_node.children.is_empty() && root_node.properties.is_empty() {
1185            return None;
1186        }
1187        return Some(root_node);
1188    }
1189    None
1190}
1191
1192fn filter_hierarchy_helper<Key>(
1193    node: &mut DiagnosticsHierarchy<Key>,
1194    hierarchy_matchers: &[&HierarchyMatcher],
1195) -> bool
1196where
1197    Key: AsRef<str>,
1198{
1199    let child_matchers = eval_matchers_on_node_name(&node.name, hierarchy_matchers);
1200    if child_matchers.is_empty() {
1201        node.children.clear();
1202        node.properties.clear();
1203        return false;
1204    }
1205
1206    if child_matchers.iter().any(|m| m.subtree) {
1207        return true;
1208    }
1209
1210    node.children.retain_mut(|child| filter_hierarchy_helper(child, &child_matchers));
1211    node.properties.retain_mut(|prop| eval_matchers_on_property(prop.name(), &child_matchers));
1212
1213    !(node.children.is_empty() && node.properties.is_empty())
1214}
1215
1216fn eval_matchers_on_node_name<'a>(
1217    node_name: &'a str,
1218    matchers: &'a [&'a HierarchyMatcher],
1219) -> Vec<&'a HierarchyMatcher> {
1220    let mut result = vec![];
1221    for matcher in matchers {
1222        for (node_pattern, tree_matcher) in matcher.nodes.iter() {
1223            if selectors::match_string(&node_pattern.0, node_name) {
1224                result.push(tree_matcher);
1225            }
1226        }
1227    }
1228    result
1229}
1230
1231fn eval_matchers_on_property(property_name: &str, matchers: &[&HierarchyMatcher]) -> bool {
1232    matchers.iter().any(|matcher| {
1233        matcher
1234            .properties
1235            .iter()
1236            .any(|property_pattern| selectors::match_string(&property_pattern.0, property_name))
1237    })
1238}
1239
1240/// The parameters of an exponential histogram.
1241#[derive(Clone)]
1242pub struct ExponentialHistogramParams<T: Clone> {
1243    /// The floor of the exponential histogram.
1244    pub floor: T,
1245
1246    /// The initial step of the exponential histogram.
1247    pub initial_step: T,
1248
1249    /// The step multiplier of the exponential histogram.
1250    pub step_multiplier: T,
1251
1252    /// The number of buckets that the exponential histogram can have. This doesn't include the
1253    /// overflow and underflow buckets.
1254    pub buckets: usize,
1255}
1256
1257/// The parameters of a linear histogram.
1258#[derive(Clone)]
1259pub struct LinearHistogramParams<T: Clone> {
1260    /// The floor of the linear histogram.
1261    pub floor: T,
1262
1263    /// The step size of the linear histogram.
1264    pub step_size: T,
1265
1266    /// The number of buckets that the linear histogram can have. This doesn't include the overflow
1267    /// and underflow buckets.
1268    pub buckets: usize,
1269}
1270
1271/// A type which can function as a "view" into a diagnostics hierarchy, optionally allocating a new
1272/// instance to service a request.
1273pub trait DiagnosticsHierarchyGetter<K: Clone> {
1274    fn get_diagnostics_hierarchy<'a>(
1275        &'a self,
1276    ) -> impl std::future::Future<Output = Cow<'_, DiagnosticsHierarchy<K>>>
1277    where
1278        K: 'a;
1279}
1280
1281impl<K: Clone> DiagnosticsHierarchyGetter<K> for DiagnosticsHierarchy<K> {
1282    async fn get_diagnostics_hierarchy<'a>(&'a self) -> Cow<'_, DiagnosticsHierarchy<K>>
1283    where
1284        K: 'a,
1285    {
1286        Cow::Borrowed(self)
1287    }
1288}
1289
1290#[cfg(test)]
1291mod tests {
1292    use super::*;
1293    use crate::testing::CondensableOnDemand;
1294    use test_case::test_case;
1295
1296    use assert_matches::assert_matches;
1297    use selectors::VerboseError;
1298    use std::sync::Arc;
1299
1300    fn validate_hierarchy_iteration(
1301        mut results_vec: Vec<(Vec<String>, Option<Property>)>,
1302        test_hierarchy: DiagnosticsHierarchy,
1303    ) {
1304        let expected_num_entries = results_vec.len();
1305        let mut num_entries = 0;
1306        for (key, val) in test_hierarchy.property_iter() {
1307            num_entries += 1;
1308            let (expected_key, expected_property) = results_vec.pop().unwrap();
1309            assert_eq!(key.to_vec().join("/"), expected_key.to_vec().join("/"));
1310            assert_eq!(val, expected_property.as_ref());
1311        }
1312
1313        assert_eq!(num_entries, expected_num_entries);
1314    }
1315
1316    #[track_caller]
1317    fn validate_hierarchy_error_iteration(
1318        mut results_vec: Vec<(Vec<String>, Option<MissingValue>)>,
1319        test_hierarchy: DiagnosticsHierarchy,
1320    ) {
1321        let expected_num_entries = results_vec.len();
1322        let mut num_entries = 0;
1323        for (key, reason) in test_hierarchy.error_iter() {
1324            num_entries += 1;
1325            let (expected_key, expected_reason) = results_vec.pop().unwrap();
1326            assert_eq!(reason, expected_reason.as_ref());
1327            assert_eq!(key.to_vec().join("/"), expected_key.to_vec().join("/"));
1328        }
1329
1330        assert_eq!(num_entries, expected_num_entries);
1331    }
1332
1333    #[fuchsia::test]
1334    fn test_diagnostics_hierarchy_property_iteration() {
1335        let double_array_data = vec![-1.2, 2.3, 3.4, 4.5, -5.6];
1336        let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
1337        let string_data = chars.iter().cycle().take(6000).collect::<String>();
1338        let bytes_data = (0u8..=9u8).cycle().take(5000).collect::<Vec<u8>>();
1339
1340        let test_hierarchy = DiagnosticsHierarchy::new(
1341            "root".to_string(),
1342            vec![
1343                Property::Int("int-root".to_string(), 3),
1344                Property::DoubleArray(
1345                    "property-double-array".to_string(),
1346                    ArrayContent::Values(double_array_data.clone()),
1347                ),
1348            ],
1349            vec![DiagnosticsHierarchy::new(
1350                "child-1".to_string(),
1351                vec![
1352                    Property::Uint("property-uint".to_string(), 10),
1353                    Property::Double("property-double".to_string(), -3.4),
1354                    Property::String("property-string".to_string(), string_data.clone()),
1355                    Property::IntArray(
1356                        "property-int-array".to_string(),
1357                        ArrayContent::new(vec![1, 2, 1, 1, 1, 1, 1], ArrayFormat::LinearHistogram)
1358                            .unwrap(),
1359                    ),
1360                ],
1361                vec![DiagnosticsHierarchy::new(
1362                    "child-1-1".to_string(),
1363                    vec![
1364                        Property::Int("property-int".to_string(), -9),
1365                        Property::Bytes("property-bytes".to_string(), bytes_data.clone()),
1366                        Property::UintArray(
1367                            "property-uint-array".to_string(),
1368                            ArrayContent::new(
1369                                vec![1, 1, 2, 0, 1, 1, 2, 0, 0],
1370                                ArrayFormat::ExponentialHistogram,
1371                            )
1372                            .unwrap(),
1373                        ),
1374                    ],
1375                    vec![],
1376                )],
1377            )],
1378        );
1379
1380        let results_vec = vec![
1381            (
1382                vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1383                Some(Property::UintArray(
1384                    "property-uint-array".to_string(),
1385                    ArrayContent::new(
1386                        vec![1, 1, 2, 0, 1, 1, 2, 0, 0],
1387                        ArrayFormat::ExponentialHistogram,
1388                    )
1389                    .unwrap(),
1390                )),
1391            ),
1392            (
1393                vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1394                Some(Property::Bytes("property-bytes".to_string(), bytes_data)),
1395            ),
1396            (
1397                vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1398                Some(Property::Int("property-int".to_string(), -9)),
1399            ),
1400            (
1401                vec!["root".to_string(), "child-1".to_string()],
1402                Some(Property::IntArray(
1403                    "property-int-array".to_string(),
1404                    ArrayContent::new(vec![1, 2, 1, 1, 1, 1, 1], ArrayFormat::LinearHistogram)
1405                        .unwrap(),
1406                )),
1407            ),
1408            (
1409                vec!["root".to_string(), "child-1".to_string()],
1410                Some(Property::String("property-string".to_string(), string_data)),
1411            ),
1412            (
1413                vec!["root".to_string(), "child-1".to_string()],
1414                Some(Property::Double("property-double".to_string(), -3.4)),
1415            ),
1416            (
1417                vec!["root".to_string(), "child-1".to_string()],
1418                Some(Property::Uint("property-uint".to_string(), 10)),
1419            ),
1420            (
1421                vec!["root".to_string()],
1422                Some(Property::DoubleArray(
1423                    "property-double-array".to_string(),
1424                    ArrayContent::Values(double_array_data),
1425                )),
1426            ),
1427            (vec!["root".to_string()], Some(Property::Int("int-root".to_string(), 3))),
1428        ];
1429
1430        validate_hierarchy_iteration(results_vec, test_hierarchy);
1431    }
1432
1433    #[fuchsia::test]
1434    fn test_diagnostics_hierarchy_error_iteration() {
1435        let mut test_hierarchy = DiagnosticsHierarchy::new(
1436            "root".to_string(),
1437            vec![],
1438            vec![
1439                DiagnosticsHierarchy::new(
1440                    "child-1".to_string(),
1441                    vec![],
1442                    vec![DiagnosticsHierarchy::new("child-1-1".to_string(), vec![], vec![])],
1443                ),
1444                DiagnosticsHierarchy::new("child-2".to_string(), vec![], vec![]),
1445            ],
1446        );
1447
1448        test_hierarchy.children[0]
1449            .add_missing(MissingValueReason::LinkNeverExpanded, "child-1".to_string());
1450        test_hierarchy.children[0].children[0]
1451            .add_missing(MissingValueReason::Timeout, "child-1-1".to_string());
1452
1453        let results_vec = vec![
1454            (
1455                vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1456                Some(MissingValue {
1457                    reason: MissingValueReason::Timeout,
1458                    name: "child-1-1".to_string(),
1459                }),
1460            ),
1461            (
1462                vec!["root".to_string(), "child-1".to_string()],
1463                Some(MissingValue {
1464                    reason: MissingValueReason::LinkNeverExpanded,
1465                    name: "child-1".to_string(),
1466                }),
1467            ),
1468            (vec!["root".to_string(), "child-2".to_string()], None),
1469            (vec!["root".to_string()], None),
1470        ];
1471
1472        validate_hierarchy_error_iteration(results_vec, test_hierarchy);
1473    }
1474
1475    #[fuchsia::test]
1476    fn test_getters() {
1477        let a_prop = Property::Int("a".to_string(), 1);
1478        let b_prop = Property::Uint("b".to_string(), 2);
1479        let child2 = DiagnosticsHierarchy::new("child2".to_string(), vec![], vec![]);
1480        let child = DiagnosticsHierarchy::new(
1481            "child".to_string(),
1482            vec![b_prop.clone()],
1483            vec![child2.clone()],
1484        );
1485        let mut hierarchy = DiagnosticsHierarchy::new(
1486            "root".to_string(),
1487            vec![a_prop.clone()],
1488            vec![child.clone()],
1489        );
1490        assert_matches!(hierarchy.get_child("child"), Some(node) if *node == child);
1491        assert_matches!(hierarchy.get_child_mut("child"), Some(node) if *node == child);
1492        assert_matches!(hierarchy.get_child_by_path(&["child", "child2"]),
1493                        Some(node) if *node == child2);
1494        assert_matches!(hierarchy.get_child_by_path_mut(&["child", "child2"]),
1495                        Some(node) if *node == child2);
1496        assert_matches!(hierarchy.get_property("a"), Some(prop) if *prop == a_prop);
1497        assert_matches!(hierarchy.get_property_by_path(&["child", "b"]),
1498                        Some(prop) if *prop == b_prop);
1499    }
1500
1501    #[fuchsia::test]
1502    fn test_edge_case_hierarchy_iteration() {
1503        let root_only_with_one_property_hierarchy = DiagnosticsHierarchy::new(
1504            "root".to_string(),
1505            vec![Property::Int("property-int".to_string(), -9)],
1506            vec![],
1507        );
1508
1509        let results_vec =
1510            vec![(vec!["root".to_string()], Some(Property::Int("property-int".to_string(), -9)))];
1511
1512        validate_hierarchy_iteration(results_vec, root_only_with_one_property_hierarchy);
1513
1514        let empty_hierarchy = DiagnosticsHierarchy::new("root".to_string(), vec![], vec![]);
1515
1516        let results_vec = vec![(vec!["root".to_string()], None)];
1517
1518        validate_hierarchy_iteration(results_vec, empty_hierarchy);
1519
1520        let empty_root_populated_child = DiagnosticsHierarchy::new(
1521            "root",
1522            vec![],
1523            vec![DiagnosticsHierarchy::new(
1524                "foo",
1525                vec![Property::Int("11".to_string(), -4)],
1526                vec![],
1527            )],
1528        );
1529
1530        let results_vec = vec![
1531            (
1532                vec!["root".to_string(), "foo".to_string()],
1533                Some(Property::Int("11".to_string(), -4)),
1534            ),
1535            (vec!["root".to_string()], None),
1536        ];
1537
1538        validate_hierarchy_iteration(results_vec, empty_root_populated_child);
1539
1540        let empty_root_empty_child = DiagnosticsHierarchy::new(
1541            "root",
1542            vec![],
1543            vec![DiagnosticsHierarchy::new("foo", vec![], vec![])],
1544        );
1545
1546        let results_vec = vec![
1547            (vec!["root".to_string(), "foo".to_string()], None),
1548            (vec!["root".to_string()], None),
1549        ];
1550
1551        validate_hierarchy_iteration(results_vec, empty_root_empty_child);
1552    }
1553
1554    #[fuchsia::test]
1555    fn array_value() {
1556        let values = vec![1, 2, 5, 7, 9, 11, 13];
1557        let array = ArrayContent::<u64>::new(values.clone(), ArrayFormat::Default);
1558        assert_matches!(array, Ok(ArrayContent::Values(vals)) if vals == values);
1559    }
1560
1561    #[fuchsia::test]
1562    fn linear_histogram_array_value() {
1563        let values = vec![1, 2, 5, 7, 9, 11, 13];
1564        let array = ArrayContent::<i64>::new(values, ArrayFormat::LinearHistogram);
1565        assert_matches!(array, Ok(ArrayContent::LinearHistogram(hist))
1566            if hist == LinearHistogram {
1567                floor: 1,
1568                step: 2,
1569                counts: vec![5, 7, 9, 11, 13],
1570                indexes: None,
1571                size: 5,
1572            }
1573        );
1574    }
1575
1576    #[fuchsia::test]
1577    fn exponential_histogram_array_value() {
1578        let values = vec![1.0, 2.0, 5.0, 7.0, 9.0, 11.0, 15.0];
1579        let array = ArrayContent::<f64>::new(values, ArrayFormat::ExponentialHistogram);
1580        assert_matches!(array, Ok(ArrayContent::ExponentialHistogram(hist))
1581            if hist == ExponentialHistogram {
1582                floor: 1.0,
1583                initial_step: 2.0,
1584                step_multiplier: 5.0,
1585                counts: vec![7.0, 9.0, 11.0, 15.0],
1586                indexes: None,
1587                size: 4,
1588            }
1589        );
1590    }
1591
1592    #[fuchsia::test]
1593    fn deserialize_linear_int_histogram() -> Result<(), serde_json::Error> {
1594        let json = r#"{
1595            "root": {
1596                "histogram": {
1597                    "floor": -2,
1598                    "step": 3,
1599                    "counts": [4, 5, 6],
1600                    "size": 3
1601                }
1602            }
1603        }"#;
1604        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1605        let expected = DiagnosticsHierarchy::new(
1606            "root".to_string(),
1607            vec![Property::IntArray(
1608                "histogram".to_string(),
1609                ArrayContent::new(vec![-2, 3, 4, 5, 6], ArrayFormat::LinearHistogram).unwrap(),
1610            )],
1611            vec![],
1612        );
1613        assert_eq!(parsed, expected);
1614        Ok(())
1615    }
1616
1617    #[fuchsia::test]
1618    fn deserialize_exponential_int_histogram() -> Result<(), serde_json::Error> {
1619        let json = r#"{
1620            "root": {
1621                "histogram": {
1622                    "floor": 1,
1623                    "initial_step": 3,
1624                    "step_multiplier": 2,
1625                    "counts": [4, 5, 6],
1626                    "size": 3
1627                }
1628            }
1629        }"#;
1630        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1631        let expected = DiagnosticsHierarchy::new(
1632            "root".to_string(),
1633            vec![Property::IntArray(
1634                "histogram".to_string(),
1635                ArrayContent::new(vec![1, 3, 2, 4, 5, 6], ArrayFormat::ExponentialHistogram)
1636                    .unwrap(),
1637            )],
1638            vec![],
1639        );
1640        assert_eq!(parsed, expected);
1641        Ok(())
1642    }
1643
1644    #[fuchsia::test]
1645    fn deserialize_linear_uint_histogram() -> Result<(), serde_json::Error> {
1646        let json = r#"{
1647            "root": {
1648                "histogram": {
1649                    "floor": 2,
1650                    "step": 3,
1651                    "counts": [4, 9223372036854775808, 6],
1652                    "size": 3
1653                }
1654            }
1655        }"#;
1656        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1657        let expected = DiagnosticsHierarchy::new(
1658            "root".to_string(),
1659            vec![Property::UintArray(
1660                "histogram".to_string(),
1661                ArrayContent::new(
1662                    vec![2, 3, 4, 9_223_372_036_854_775_808, 6],
1663                    ArrayFormat::LinearHistogram,
1664                )
1665                .unwrap(),
1666            )],
1667            vec![],
1668        );
1669        assert_eq!(parsed, expected);
1670        Ok(())
1671    }
1672
1673    #[fuchsia::test]
1674    fn deserialize_linear_double_histogram() -> Result<(), serde_json::Error> {
1675        let json = r#"{
1676            "root": {
1677                "histogram": {
1678                    "floor": 2.0,
1679                    "step": 3.0,
1680                    "counts": [4.0, 5.0, 6.0],
1681                    "size": 3
1682                }
1683            }
1684        }"#;
1685        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1686        let expected = DiagnosticsHierarchy::new(
1687            "root".to_string(),
1688            vec![Property::DoubleArray(
1689                "histogram".to_string(),
1690                ArrayContent::new(vec![2.0, 3.0, 4.0, 5.0, 6.0], ArrayFormat::LinearHistogram)
1691                    .unwrap(),
1692            )],
1693            vec![],
1694        );
1695        assert_eq!(parsed, expected);
1696        Ok(())
1697    }
1698
1699    #[fuchsia::test]
1700    fn deserialize_sparse_histogram() -> Result<(), serde_json::Error> {
1701        let json = r#"{
1702            "root": {
1703                "histogram": {
1704                    "floor": 2,
1705                    "step": 3,
1706                    "counts": [4, 5, 6],
1707                    "indexes": [1, 2, 4],
1708                    "size": 8
1709                }
1710            }
1711        }"#;
1712        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1713
1714        let mut histogram =
1715            ArrayContent::new(vec![2, 3, 0, 4, 5, 0, 6, 0, 0, 0], ArrayFormat::LinearHistogram)
1716                .unwrap();
1717        histogram.condense_histogram();
1718        let expected = DiagnosticsHierarchy::new(
1719            "root".to_string(),
1720            vec![Property::IntArray("histogram".to_string(), histogram)],
1721            vec![],
1722        );
1723        assert_eq!(parsed, expected);
1724        Ok(())
1725    }
1726
1727    // If a struct can't be parsed as a valid histogram, it will be created as a Node. So if
1728    // there's a node "histogram" (as opposed to a property "histogram") then it didn't parse
1729    // as a histogram.
1730
1731    #[fuchsia::test]
1732    fn reject_histogram_incompatible_values() -> Result<(), serde_json::Error> {
1733        let json = r#"{
1734            "root": {
1735                "histogram": {
1736                    "floor": -2,
1737                    "step": 3,
1738                    "counts": [4, 9223372036854775808, 6],
1739                    "size": 3
1740                }
1741            }
1742        }"#;
1743        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1744        assert_eq!(parsed.children.len(), 1);
1745        assert_eq!(&parsed.children[0].name, "histogram");
1746        Ok(())
1747    }
1748
1749    #[fuchsia::test]
1750    fn reject_histogram_bad_sparse_list() -> Result<(), serde_json::Error> {
1751        let json = r#"{
1752            "root": {
1753                "histogram": {
1754                    "floor": -2,
1755                    "step": 3,
1756                    "counts": [4, 5, 6],
1757                    "indexes": [0, 1, 2, 3],
1758                    "size": 8
1759                }
1760            }
1761        }"#;
1762        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1763        assert_eq!(parsed.children.len(), 1);
1764        assert_eq!(&parsed.children[0].name, "histogram");
1765        Ok(())
1766    }
1767
1768    #[fuchsia::test]
1769    fn reject_histogram_bad_index() -> Result<(), serde_json::Error> {
1770        let json = r#"{
1771            "root": {
1772                "histogram": {
1773                    "floor": -2,
1774                    "step": 3,
1775                    "counts": [4, 5, 6],
1776                    "indexes": [0, 1, 4],
1777                    "size": 4
1778                }
1779            }
1780        }"#;
1781        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1782        assert_eq!(parsed.children.len(), 1);
1783        assert_eq!(&parsed.children[0].name, "histogram");
1784        Ok(())
1785    }
1786
1787    #[fuchsia::test]
1788    fn reject_histogram_wrong_field() -> Result<(), serde_json::Error> {
1789        let json = r#"{
1790            "root": {
1791                "histogram": {
1792                    "floor": 2,
1793                    "step": 3,
1794                    "counts": [4, 5, 6],
1795                    "incorrect": [0, 1, 3],
1796                    "size": 4
1797                }
1798            }
1799        }"#;
1800        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1801        assert_eq!(parsed.children.len(), 1);
1802        assert_eq!(&parsed.children[0].name, "histogram");
1803        Ok(())
1804    }
1805
1806    #[fuchsia::test]
1807    fn exponential_histogram() {
1808        let values = vec![0, 2, 4, 0, 1, 2, 3, 4, 5];
1809        let array = ArrayContent::new(values, ArrayFormat::ExponentialHistogram);
1810        assert_matches!(array, Ok(ArrayContent::ExponentialHistogram(hist))
1811            if hist == ExponentialHistogram {
1812                floor: 0,
1813                initial_step: 2,
1814                step_multiplier: 4,
1815                counts: vec![0, 1, 2, 3, 4, 5],
1816                indexes: None,
1817                size: 6,
1818            }
1819        );
1820    }
1821
1822    #[fuchsia::test]
1823    fn add_to_hierarchy() {
1824        let mut hierarchy = DiagnosticsHierarchy::new_root();
1825        let prop_1 = Property::String("x".to_string(), "foo".to_string());
1826        let path_1 = vec!["root", "one"];
1827        let prop_2 = Property::Uint("c".to_string(), 3);
1828        let path_2 = vec!["root", "two"];
1829        let prop_2_prime = Property::Int("z".to_string(), -4);
1830        hierarchy.add_property_at_path(&path_1, prop_1.clone());
1831        hierarchy.add_property_at_path(&path_2.clone(), prop_2.clone());
1832        hierarchy.add_property_at_path(&path_2, prop_2_prime.clone());
1833
1834        assert_eq!(
1835            hierarchy,
1836            DiagnosticsHierarchy {
1837                name: "root".to_string(),
1838                children: vec![
1839                    DiagnosticsHierarchy {
1840                        name: "one".to_string(),
1841                        properties: vec![prop_1],
1842                        children: vec![],
1843                        missing: vec![],
1844                    },
1845                    DiagnosticsHierarchy {
1846                        name: "two".to_string(),
1847                        properties: vec![prop_2, prop_2_prime],
1848                        children: vec![],
1849                        missing: vec![],
1850                    }
1851                ],
1852                properties: vec![],
1853                missing: vec![],
1854            }
1855        );
1856    }
1857
1858    #[fuchsia::test]
1859    fn string_lists() {
1860        let mut hierarchy = DiagnosticsHierarchy::new_root();
1861        let prop_1 =
1862            Property::StringList("x".to_string(), vec!["foo".to_string(), "bar".to_string()]);
1863        let path_1 = vec!["root", "one"];
1864        hierarchy.add_property_at_path(&path_1, prop_1.clone());
1865
1866        assert_eq!(
1867            hierarchy,
1868            DiagnosticsHierarchy {
1869                name: "root".to_string(),
1870                children: vec![DiagnosticsHierarchy {
1871                    name: "one".to_string(),
1872                    properties: vec![prop_1],
1873                    children: vec![],
1874                    missing: vec![],
1875                },],
1876                properties: vec![],
1877                missing: vec![],
1878            }
1879        );
1880    }
1881
1882    #[fuchsia::test]
1883    // TODO(https://fxbug.dev/42169733): delete the below
1884    #[cfg_attr(feature = "variant_asan", ignore)]
1885    #[cfg_attr(feature = "variant_hwasan", ignore)]
1886    #[should_panic]
1887    // Empty paths are meaningless on insertion and break the method invariant.
1888    fn no_empty_paths_allowed() {
1889        let mut hierarchy = DiagnosticsHierarchy::<String>::new_root();
1890        let path_1: Vec<&String> = vec![];
1891        hierarchy.get_or_add_node(&path_1);
1892    }
1893
1894    #[fuchsia::test]
1895    #[should_panic]
1896    // Paths provided to add must begin at the node we're calling
1897    // add() on.
1898    fn path_must_start_at_self() {
1899        let mut hierarchy = DiagnosticsHierarchy::<String>::new_root();
1900        let path_1 = vec!["not_root", "a"];
1901        hierarchy.get_or_add_node(&path_1);
1902    }
1903
1904    #[fuchsia::test]
1905    fn sort_hierarchy() {
1906        let mut hierarchy = DiagnosticsHierarchy::new(
1907            "root",
1908            vec![
1909                Property::String("x".to_string(), "foo".to_string()),
1910                Property::Uint("c".to_string(), 3),
1911                Property::Int("z".to_string(), -4),
1912            ],
1913            vec![
1914                DiagnosticsHierarchy::new(
1915                    "foo",
1916                    vec![
1917                        Property::Int("11".to_string(), -4),
1918                        Property::Bytes("123".to_string(), "foo".bytes().collect()),
1919                        Property::Double("0".to_string(), 8.1),
1920                    ],
1921                    vec![],
1922                ),
1923                DiagnosticsHierarchy::new("bar", vec![], vec![]),
1924            ],
1925        );
1926
1927        hierarchy.sort();
1928
1929        let sorted_hierarchy = DiagnosticsHierarchy::new(
1930            "root",
1931            vec![
1932                Property::Uint("c".to_string(), 3),
1933                Property::String("x".to_string(), "foo".to_string()),
1934                Property::Int("z".to_string(), -4),
1935            ],
1936            vec![
1937                DiagnosticsHierarchy::new("bar", vec![], vec![]),
1938                DiagnosticsHierarchy::new(
1939                    "foo",
1940                    vec![
1941                        Property::Double("0".to_string(), 8.1),
1942                        Property::Int("11".to_string(), -4),
1943                        Property::Bytes("123".to_string(), "foo".bytes().collect()),
1944                    ],
1945                    vec![],
1946                ),
1947            ],
1948        );
1949        assert_eq!(sorted_hierarchy, hierarchy);
1950    }
1951
1952    fn parse_selectors_and_filter_hierarchy(
1953        hierarchy: DiagnosticsHierarchy,
1954        test_selectors: Vec<&str>,
1955    ) -> Option<DiagnosticsHierarchy> {
1956        let parsed_test_selectors = test_selectors
1957            .into_iter()
1958            .map(|selector_string| {
1959                Arc::new(
1960                    selectors::parse_selector::<VerboseError>(selector_string)
1961                        .expect("All test selectors are valid and parsable."),
1962                )
1963            })
1964            .collect::<Vec<Arc<Selector>>>();
1965
1966        let hierarchy_matcher: HierarchyMatcher = parsed_test_selectors.try_into().unwrap();
1967
1968        filter_hierarchy(hierarchy, &hierarchy_matcher).map(|mut hierarchy| {
1969            hierarchy.sort();
1970            hierarchy
1971        })
1972    }
1973
1974    fn get_test_hierarchy() -> DiagnosticsHierarchy {
1975        DiagnosticsHierarchy::new(
1976            "root",
1977            vec![
1978                Property::String("x".to_string(), "foo".to_string()),
1979                Property::Uint("c".to_string(), 3),
1980                Property::Int("z".to_string(), -4),
1981            ],
1982            vec![
1983                make_foo(),
1984                DiagnosticsHierarchy::new(
1985                    "bar",
1986                    vec![Property::Int("12".to_string(), -4)],
1987                    vec![DiagnosticsHierarchy::new(
1988                        "zed",
1989                        vec![Property::Int("13/:".to_string(), -4)],
1990                        vec![],
1991                    )],
1992                ),
1993            ],
1994        )
1995    }
1996
1997    fn make_all_foo_props() -> Vec<Property> {
1998        vec![
1999            Property::Int("11".to_string(), -4),
2000            Property::Bytes("123".to_string(), b"foo".to_vec()),
2001            Property::Double("0".to_string(), 8.1),
2002        ]
2003    }
2004
2005    fn make_zed() -> Vec<DiagnosticsHierarchy> {
2006        vec![DiagnosticsHierarchy::new("zed", vec![Property::Int("13".to_string(), -4)], vec![])]
2007    }
2008
2009    fn make_foo() -> DiagnosticsHierarchy {
2010        DiagnosticsHierarchy::new("foo", make_all_foo_props(), make_zed())
2011    }
2012
2013    #[fuchsia::test]
2014    fn test_filter_hierarchy() {
2015        let test_selectors = vec!["*:root/foo:11", "*:root:z", r#"*:root/bar/zed:13\/\:"#];
2016
2017        assert_eq!(
2018            parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2019            Some(DiagnosticsHierarchy::new(
2020                "root",
2021                vec![Property::Int("z".to_string(), -4),],
2022                vec![
2023                    DiagnosticsHierarchy::new(
2024                        "bar",
2025                        vec![],
2026                        vec![DiagnosticsHierarchy::new(
2027                            "zed",
2028                            vec![Property::Int("13/:".to_string(), -4)],
2029                            vec![],
2030                        )],
2031                    ),
2032                    DiagnosticsHierarchy::new(
2033                        "foo",
2034                        vec![Property::Int("11".to_string(), -4),],
2035                        vec![],
2036                    )
2037                ],
2038            ))
2039        );
2040
2041        let test_selectors = vec!["*:root"];
2042        let mut sorted_expected = get_test_hierarchy();
2043        sorted_expected.sort();
2044        assert_eq!(
2045            parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2046            Some(sorted_expected)
2047        );
2048    }
2049
2050    #[fuchsia::test]
2051    fn test_filter_does_not_include_empty_node() {
2052        let test_selectors = vec!["*:root/foo:blorg"];
2053
2054        assert_eq!(
2055            parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2056            None,
2057        );
2058    }
2059
2060    #[fuchsia::test]
2061    fn test_filter_empty_hierarchy() {
2062        let test_selectors = vec!["*:root"];
2063
2064        assert_eq!(
2065            parse_selectors_and_filter_hierarchy(
2066                DiagnosticsHierarchy::new("root", vec![], vec![]),
2067                test_selectors
2068            ),
2069            Some(DiagnosticsHierarchy::new("root", vec![], vec![])),
2070        );
2071    }
2072
2073    #[fuchsia::test]
2074    fn test_full_filtering() {
2075        // If we select a non-existent root, then we return a fully filtered hierarchy.
2076        let test_selectors = vec!["*:non-existent-root"];
2077        assert_eq!(
2078            parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2079            None,
2080        );
2081
2082        // If we select a non-existent child of the root, then we return a fully filtered hierarchy.
2083        let test_selectors = vec!["*:root/i-dont-exist:foo"];
2084        assert_eq!(
2085            parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2086            None,
2087        );
2088
2089        // Even if the root exists, but we don't include any property, we consider the hierarchy
2090        // fully filtered. This is aligned with the previous case.
2091        let test_selectors = vec!["*:root:i-dont-exist"];
2092        assert_eq!(
2093            parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2094            None,
2095        );
2096    }
2097
2098    #[fuchsia::test]
2099    fn test_subtree_selection_includes_empty_nodes() {
2100        let test_selectors = vec!["*:root"];
2101        let mut empty_hierarchy = DiagnosticsHierarchy::new(
2102            "root",
2103            vec![],
2104            vec![
2105                DiagnosticsHierarchy::new(
2106                    "foo",
2107                    vec![],
2108                    vec![DiagnosticsHierarchy::new("zed", vec![], vec![])],
2109                ),
2110                DiagnosticsHierarchy::new(
2111                    "bar",
2112                    vec![],
2113                    vec![DiagnosticsHierarchy::new("zed", vec![], vec![])],
2114                ),
2115            ],
2116        );
2117
2118        empty_hierarchy.sort();
2119
2120        assert_eq!(
2121            parse_selectors_and_filter_hierarchy(empty_hierarchy.clone(), test_selectors),
2122            Some(empty_hierarchy)
2123        );
2124    }
2125
2126    #[fuchsia::test]
2127    fn test_empty_tree_filtering() {
2128        // Subtree selection on the empty tree should produce the empty tree.
2129        let mut empty_hierarchy = DiagnosticsHierarchy::new("root", vec![], vec![]);
2130        empty_hierarchy.sort();
2131
2132        let subtree_selector = vec!["*:root"];
2133        assert_eq!(
2134            parse_selectors_and_filter_hierarchy(empty_hierarchy.clone(), subtree_selector),
2135            Some(empty_hierarchy.clone())
2136        );
2137
2138        // Selecting a property on the root, even if it doesn't exist, should produce nothing.
2139        let fake_property_selector = vec!["*:root:blorp"];
2140        assert_eq!(
2141            parse_selectors_and_filter_hierarchy(empty_hierarchy.clone(), fake_property_selector),
2142            None,
2143        );
2144    }
2145
2146    #[test_case(vec![Property::Int("11".to_string(), -4)], "root/foo:11" ; "specific_property")]
2147    #[test_case(make_all_foo_props(), "root/foo:*" ; "many_properties")]
2148    #[test_case(vec![], "root/foo:none" ; "property_not_there")]
2149    #[fuchsia::test]
2150    fn test_select_from_hierarchy_property_selectors(expected: Vec<Property>, tree_selector: &str) {
2151        let hierarchy = get_test_hierarchy();
2152        let parsed_selector =
2153            selectors::parse_selector::<VerboseError>(&format!("*:{tree_selector}"))
2154                .expect("All test selectors are valid and parsable.");
2155        let Ok(SelectResult::Properties(mut property_entry_vec)) =
2156            select_from_hierarchy(&hierarchy, &parsed_selector)
2157        else {
2158            panic!("must be properties");
2159        };
2160
2161        property_entry_vec.sort_by(|p1, p2| p1.name().cmp(p2.name()));
2162        let mut expected = expected.iter().map(Cow::Borrowed).collect::<Vec<_>>();
2163        expected.sort_by(|p1, p2| p1.name().cmp(p2.name()));
2164
2165        assert_eq!(property_entry_vec, expected);
2166    }
2167
2168    #[test_case(vec![], "root/none" ; "node_not_there")]
2169    #[test_case(make_zed(), "root/foo/zed" ; "properties_only")]
2170    #[test_case(vec![make_foo()], "root/foo" ; "nodes_and_properties")]
2171    #[test_case(vec![get_test_hierarchy()], "root" ; "select_root")]
2172    #[fuchsia::test]
2173    fn test_select_from_hierarchy_tree_selectors(
2174        expected: Vec<DiagnosticsHierarchy>,
2175        tree_selector: &str,
2176    ) {
2177        let hierarchy = get_test_hierarchy();
2178        let parsed_selector =
2179            selectors::parse_selector::<VerboseError>(&format!("*:{tree_selector}"))
2180                .expect("All test selectors are valid and parsable.");
2181        let Ok(SelectResult::Nodes(node_vec)) = select_from_hierarchy(&hierarchy, &parsed_selector)
2182        else {
2183            panic!("must be nodes");
2184        };
2185
2186        let expected = expected.iter().map(Cow::Borrowed).collect::<Vec<_>>();
2187
2188        assert_eq!(node_vec, expected);
2189    }
2190
2191    #[fuchsia::test]
2192    fn sort_numerical_value() {
2193        let mut diagnostics_hierarchy = DiagnosticsHierarchy::new(
2194            "root",
2195            vec![
2196                Property::Double("2".to_string(), 2.3),
2197                Property::Int("0".to_string(), -4),
2198                Property::Uint("10".to_string(), 3),
2199                Property::String("1".to_string(), "test".to_string()),
2200            ],
2201            vec![
2202                DiagnosticsHierarchy::new("123", vec![], vec![]),
2203                DiagnosticsHierarchy::new("34", vec![], vec![]),
2204                DiagnosticsHierarchy::new("4", vec![], vec![]),
2205                DiagnosticsHierarchy::new("023", vec![], vec![]),
2206                DiagnosticsHierarchy::new("12", vec![], vec![]),
2207                DiagnosticsHierarchy::new("1", vec![], vec![]),
2208            ],
2209        );
2210        diagnostics_hierarchy.sort();
2211        assert_eq!(
2212            diagnostics_hierarchy,
2213            DiagnosticsHierarchy::new(
2214                "root",
2215                vec![
2216                    Property::Int("0".to_string(), -4),
2217                    Property::String("1".to_string(), "test".to_string()),
2218                    Property::Double("2".to_string(), 2.3),
2219                    Property::Uint("10".to_string(), 3),
2220                ],
2221                vec![
2222                    DiagnosticsHierarchy::new("1", vec![], vec![]),
2223                    DiagnosticsHierarchy::new("4", vec![], vec![]),
2224                    DiagnosticsHierarchy::new("12", vec![], vec![]),
2225                    DiagnosticsHierarchy::new("023", vec![], vec![]),
2226                    DiagnosticsHierarchy::new("34", vec![], vec![]),
2227                    DiagnosticsHierarchy::new("123", vec![], vec![]),
2228                ]
2229            )
2230        );
2231    }
2232
2233    #[fuchsia::test]
2234    fn filter_hierarchy_doesnt_return_partial_matches() {
2235        let hierarchy = DiagnosticsHierarchy::new(
2236            "root",
2237            vec![],
2238            vec![DiagnosticsHierarchy::new("session_started_at", vec![], vec![])],
2239        );
2240        let test_selectors = vec!["*:root/session_started_at/0"];
2241        assert_eq!(parse_selectors_and_filter_hierarchy(hierarchy, test_selectors), None);
2242    }
2243
2244    #[fuchsia::test]
2245    fn test_filter_tree() {
2246        let test_selectors = vec!["root/foo:11", "root:z", r#"root/bar/zed:13\/\:"#];
2247        let parsed_test_selectors = test_selectors
2248            .into_iter()
2249            .map(|s| {
2250                selectors::parse_tree_selector::<VerboseError>(s)
2251                    .expect("All test selectors are valid and parsable.")
2252            })
2253            .collect::<Vec<_>>();
2254
2255        let result =
2256            filter_tree(get_test_hierarchy(), &parsed_test_selectors).map(|mut hierarchy| {
2257                hierarchy.sort();
2258                hierarchy
2259            });
2260        assert_eq!(
2261            result,
2262            Some(DiagnosticsHierarchy::new(
2263                "root",
2264                vec![Property::Int("z".to_string(), -4),],
2265                vec![
2266                    DiagnosticsHierarchy::new(
2267                        "bar",
2268                        vec![],
2269                        vec![DiagnosticsHierarchy::new(
2270                            "zed",
2271                            vec![Property::Int("13/:".to_string(), -4)],
2272                            vec![],
2273                        )],
2274                    ),
2275                    DiagnosticsHierarchy::new(
2276                        "foo",
2277                        vec![Property::Int("11".to_string(), -4),],
2278                        vec![],
2279                    )
2280                ],
2281            ))
2282        );
2283    }
2284
2285    #[fuchsia::test]
2286    fn test_matcher_from_iterator() {
2287        let matcher = HierarchyMatcher::new(
2288            ["*:root/foo:11", "*:root:z", r#"*:root/bar/zed:13\/\:"#].into_iter().map(|s| {
2289                selectors::parse_selector::<VerboseError>(s)
2290                    .expect("All test selectors are valid and parsable.")
2291            }),
2292        )
2293        .expect("create matcher from iterator of selectors");
2294        let result = filter_hierarchy(get_test_hierarchy(), &matcher).map(|mut hierarchy| {
2295            hierarchy.sort();
2296            hierarchy
2297        });
2298        assert_eq!(
2299            result,
2300            Some(DiagnosticsHierarchy::new(
2301                "root",
2302                vec![Property::Int("z".to_string(), -4),],
2303                vec![
2304                    DiagnosticsHierarchy::new(
2305                        "bar",
2306                        vec![],
2307                        vec![DiagnosticsHierarchy::new(
2308                            "zed",
2309                            vec![Property::Int("13/:".to_string(), -4)],
2310                            vec![],
2311                        )],
2312                    ),
2313                    DiagnosticsHierarchy::new(
2314                        "foo",
2315                        vec![Property::Int("11".to_string(), -4),],
2316                        vec![],
2317                    )
2318                ],
2319            ))
2320        );
2321    }
2322
2323    #[test_case(DiagnosticsHierarchy::new_root() ; "empty")]
2324    #[test_case(DiagnosticsHierarchy::new(
2325        "root",
2326        vec![Property::String("x".to_string(), "foo".to_string())],
2327        vec![],
2328    ) ; "properties")]
2329    #[test_case(DiagnosticsHierarchy::new(
2330        "root",
2331        vec![],
2332        vec![DiagnosticsHierarchy::new_root()],
2333    ) ; "children")]
2334    #[fuchsia::test]
2335    fn test_merge_hierarchy_empty(other: DiagnosticsHierarchy) {
2336        let mut hierarchy = DiagnosticsHierarchy::new_root();
2337        hierarchy.merge(other.clone());
2338        assert_eq!(hierarchy, other);
2339    }
2340
2341    #[fuchsia::test]
2342    fn test_merge_hierarchy_properties_and_children() {
2343        let mut a = DiagnosticsHierarchy::new(
2344            "root",
2345            vec![Property::Int("a".to_string(), 1)],
2346            vec![DiagnosticsHierarchy::new("child_a", vec![], vec![])],
2347        );
2348        let b = DiagnosticsHierarchy::new(
2349            "root",
2350            vec![Property::String("b".to_string(), "foo".to_string())],
2351            vec![DiagnosticsHierarchy::new("child_b", vec![], vec![])],
2352        );
2353        a.merge(b);
2354        let expected = DiagnosticsHierarchy::new(
2355            "root",
2356            vec![
2357                Property::Int("a".to_string(), 1),
2358                Property::String("b".to_string(), "foo".to_string()),
2359            ],
2360            vec![
2361                DiagnosticsHierarchy::new("child_a", vec![], vec![]),
2362                DiagnosticsHierarchy::new("child_b", vec![], vec![]),
2363            ],
2364        );
2365        assert_eq!(a, expected);
2366    }
2367
2368    #[fuchsia::test]
2369    fn test_merge_hierarchy_nested_children() {
2370        let mut a = DiagnosticsHierarchy::<String>::new(
2371            "root",
2372            vec![],
2373            vec![DiagnosticsHierarchy::new(
2374                "child",
2375                vec![],
2376                vec![DiagnosticsHierarchy::new("grandchild_a", vec![], vec![])],
2377            )],
2378        );
2379        let b = DiagnosticsHierarchy::new(
2380            "root",
2381            vec![],
2382            vec![DiagnosticsHierarchy::new(
2383                "child",
2384                vec![],
2385                vec![DiagnosticsHierarchy::new("grandchild_b", vec![], vec![])],
2386            )],
2387        );
2388        a.merge(b);
2389        let expected = DiagnosticsHierarchy::new(
2390            "root",
2391            vec![],
2392            vec![DiagnosticsHierarchy::new(
2393                "child",
2394                vec![],
2395                vec![
2396                    DiagnosticsHierarchy::new("grandchild_a", vec![], vec![]),
2397                    DiagnosticsHierarchy::new("grandchild_b", vec![], vec![]),
2398                ],
2399            )],
2400        );
2401        assert_eq!(a, expected);
2402    }
2403
2404    #[fuchsia::test]
2405    fn test_merge_hierarchy_missing() {
2406        let mut a: DiagnosticsHierarchy<String> = DiagnosticsHierarchy::new_root();
2407        a.add_missing(MissingValueReason::LinkNotFound, "a".to_string());
2408        let mut b = DiagnosticsHierarchy::new_root();
2409        b.add_missing(MissingValueReason::LinkParseFailure, "b".to_string());
2410        a.merge(b);
2411        let expected = DiagnosticsHierarchy {
2412            missing: vec![
2413                MissingValue { reason: MissingValueReason::LinkNotFound, name: "a".to_string() },
2414                MissingValue {
2415                    reason: MissingValueReason::LinkParseFailure,
2416                    name: "b".to_string(),
2417                },
2418            ],
2419            ..DiagnosticsHierarchy::new_root()
2420        };
2421        assert_eq!(a, expected);
2422    }
2423
2424    #[fuchsia::test]
2425    fn test_merge_hierarchy_missing_no_duplicates() {
2426        let mut a: DiagnosticsHierarchy<String> = DiagnosticsHierarchy::new_root();
2427        a.add_missing(MissingValueReason::LinkNotFound, "a".to_string());
2428        let mut b = DiagnosticsHierarchy::new_root();
2429        b.add_missing(MissingValueReason::LinkNotFound, "a".to_string());
2430        a.merge(b);
2431        let expected = DiagnosticsHierarchy {
2432            missing: vec![MissingValue {
2433                reason: MissingValueReason::LinkNotFound,
2434                name: "a".to_string(),
2435            }],
2436            ..DiagnosticsHierarchy::new_root()
2437        };
2438        assert_eq!(a, expected);
2439    }
2440
2441    #[fuchsia::test]
2442    fn test_merge_hierarchy_overwrite_property() {
2443        let mut a = DiagnosticsHierarchy::new(
2444            "root",
2445            vec![Property::String("x".to_string(), "foo".to_string())],
2446            vec![],
2447        );
2448        let b = DiagnosticsHierarchy::new(
2449            "root",
2450            vec![Property::String("x".to_string(), "bar".to_string())],
2451            vec![],
2452        );
2453        a.merge(b);
2454        let expected = DiagnosticsHierarchy::new(
2455            "root",
2456            vec![Property::String("x".to_string(), "bar".to_string())],
2457            vec![],
2458        );
2459        assert_eq!(a, expected);
2460    }
2461
2462    #[fuchsia::test]
2463    fn test_merge_hierarchy_recursive_children() {
2464        let mut a = DiagnosticsHierarchy::new(
2465            "root",
2466            vec![],
2467            vec![DiagnosticsHierarchy::new(
2468                "child",
2469                vec![Property::Int("a".to_string(), 1)],
2470                vec![],
2471            )],
2472        );
2473        let b = DiagnosticsHierarchy::new(
2474            "root",
2475            vec![],
2476            vec![DiagnosticsHierarchy::new(
2477                "child",
2478                vec![Property::Int("b".to_string(), 2)],
2479                vec![],
2480            )],
2481        );
2482        a.merge(b);
2483        let expected = DiagnosticsHierarchy::new(
2484            "root",
2485            vec![],
2486            vec![DiagnosticsHierarchy::new(
2487                "child",
2488                vec![Property::Int("a".to_string(), 1), Property::Int("b".to_string(), 2)],
2489                vec![],
2490            )],
2491        );
2492        assert_eq!(a, expected);
2493    }
2494}