Skip to main content

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