diagnostics_hierarchy/
lib.rs

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