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> {
981    Properties(Vec<&'a Property<Key>>),
982    Nodes(Vec<&'a DiagnosticsHierarchy<Key>>),
983}
984
985impl<'a, Key> SelectResult<'a, Key> {
986    /// Returns Err(()) if `self` is `Self::Nodes`. Otherwise, adds to property list.
987    fn add_property(&mut self, prop: &'a Property<Key>) {
988        let Self::Properties(v) = self else {
989            panic!("must be Self::Properties to call add_property");
990        };
991        v.push(prop);
992    }
993
994    /// Returns Err(()) if `self` is `Self::Properties`. Otherwise, adds to property list.
995    fn add_node(&mut self, node: &'a DiagnosticsHierarchy<Key>) {
996        let Self::Nodes(v) = self else {
997            panic!("must be Self::Nodes to call add_node");
998        };
999        v.push(node);
1000    }
1001}
1002
1003pub fn select_from_hierarchy<'a, 'b, Key>(
1004    root_node: &'a DiagnosticsHierarchy<Key>,
1005    selector: &'b Selector,
1006) -> Result<SelectResult<'a, Key>, Error>
1007where
1008    Key: AsRef<str>,
1009    'a: 'b,
1010{
1011    selector.validate()?;
1012
1013    struct StackEntry<'a, Key> {
1014        node: &'a DiagnosticsHierarchy<Key>,
1015        node_path_index: usize,
1016        explored_path: Vec<&'a str>,
1017    }
1018
1019    // Safe to unwrap since we validated above.
1020    let (node_path, property_selector, stack_entry) = match selector.tree_selector.as_ref().unwrap()
1021    {
1022        TreeSelector::SubtreeSelector(ref subtree_selector) => (
1023            &subtree_selector.node_path,
1024            None,
1025            StackEntry { node: root_node, node_path_index: 0, explored_path: vec![] },
1026        ),
1027        TreeSelector::PropertySelector(ref property_selector) => (
1028            &property_selector.node_path,
1029            Some(&property_selector.target_properties),
1030            StackEntry { node: root_node, node_path_index: 0, explored_path: vec![] },
1031        ),
1032        _ => return Err(Error::InvalidTreeSelector),
1033    };
1034
1035    let mut stack = vec![stack_entry];
1036    let mut result = if property_selector.is_some() {
1037        SelectResult::Properties(vec![])
1038    } else {
1039        SelectResult::Nodes(vec![])
1040    };
1041
1042    while let Some(StackEntry { node, node_path_index, mut explored_path }) = stack.pop() {
1043        // Unwrap is safe since we validate is_empty right above.
1044        if !selectors::match_string(&node_path[node_path_index], &node.name) {
1045            continue;
1046        }
1047        explored_path.push(&node.name);
1048
1049        // If we are at the last node in the path, then we just need to explore the properties.
1050        // Otherwise, we explore the children of the current node and the properties.
1051        if node_path_index != node_path.len() - 1 {
1052            // If this node matches the next selector we are looking at, then explore its children.
1053            for child in node.children.iter() {
1054                stack.push(StackEntry {
1055                    node: child,
1056                    node_path_index: node_path_index + 1,
1057                    explored_path: explored_path.clone(),
1058                });
1059            }
1060        } else if let Some(s) = property_selector {
1061            // If we have a property selector, then add any properties matching it to our result.
1062            for property in &node.properties {
1063                if selectors::match_string(s, property.key()) {
1064                    result.add_property(property);
1065                }
1066            }
1067        } else {
1068            // If we don't have a property selector and we reached the end of the node path, then
1069            // we should add the current node to the result.
1070            result.add_node(node);
1071        }
1072    }
1073
1074    Ok(result)
1075}
1076
1077/// Filters a hierarchy given a tree selector.
1078pub fn filter_tree<Key>(
1079    root_node: DiagnosticsHierarchy<Key>,
1080    selectors: &[TreeSelector],
1081) -> Option<DiagnosticsHierarchy<Key>>
1082where
1083    Key: AsRef<str>,
1084{
1085    let mut matcher = HierarchyMatcher::default();
1086    for selector in selectors {
1087        match selector {
1088            TreeSelector::SubtreeSelector(subtree_selector) => {
1089                matcher.insert_subtree(subtree_selector.clone());
1090            }
1091            TreeSelector::PropertySelector(property_selector) => {
1092                matcher.insert_property(property_selector.clone());
1093            }
1094            _ => {}
1095        }
1096    }
1097    filter_hierarchy(root_node, &matcher)
1098}
1099
1100/// Filters a diagnostics hierarchy using a set of path selectors and their associated property
1101/// selectors.
1102///
1103/// If the return type is None that implies that the filter encountered no errors AND the tree was
1104/// filtered to be empty at the end.
1105pub fn filter_hierarchy<Key>(
1106    mut root_node: DiagnosticsHierarchy<Key>,
1107    hierarchy_matcher: &HierarchyMatcher,
1108) -> Option<DiagnosticsHierarchy<Key>>
1109where
1110    Key: AsRef<str>,
1111{
1112    let starts_empty = root_node.children.is_empty() && root_node.properties.is_empty();
1113    if filter_hierarchy_helper(&mut root_node, &[hierarchy_matcher]) {
1114        if !starts_empty && root_node.children.is_empty() && root_node.properties.is_empty() {
1115            return None;
1116        }
1117        return Some(root_node);
1118    }
1119    None
1120}
1121
1122fn filter_hierarchy_helper<Key>(
1123    node: &mut DiagnosticsHierarchy<Key>,
1124    hierarchy_matchers: &[&HierarchyMatcher],
1125) -> bool
1126where
1127    Key: AsRef<str>,
1128{
1129    let child_matchers = eval_matchers_on_node_name(&node.name, hierarchy_matchers);
1130    if child_matchers.is_empty() {
1131        node.children.clear();
1132        node.properties.clear();
1133        return false;
1134    }
1135
1136    if child_matchers.iter().any(|m| m.subtree) {
1137        return true;
1138    }
1139
1140    node.children.retain_mut(|child| filter_hierarchy_helper(child, &child_matchers));
1141    node.properties.retain_mut(|prop| eval_matchers_on_property(prop.name(), &child_matchers));
1142
1143    !(node.children.is_empty() && node.properties.is_empty())
1144}
1145
1146fn eval_matchers_on_node_name<'a>(
1147    node_name: &'a str,
1148    matchers: &'a [&'a HierarchyMatcher],
1149) -> Vec<&'a HierarchyMatcher> {
1150    let mut result = vec![];
1151    for matcher in matchers {
1152        for (node_pattern, tree_matcher) in matcher.nodes.iter() {
1153            if selectors::match_string(&node_pattern.0, node_name) {
1154                result.push(tree_matcher);
1155            }
1156        }
1157    }
1158    result
1159}
1160
1161fn eval_matchers_on_property(property_name: &str, matchers: &[&HierarchyMatcher]) -> bool {
1162    matchers.iter().any(|matcher| {
1163        matcher
1164            .properties
1165            .iter()
1166            .any(|property_pattern| selectors::match_string(&property_pattern.0, property_name))
1167    })
1168}
1169
1170/// The parameters of an exponential histogram.
1171#[derive(Clone)]
1172pub struct ExponentialHistogramParams<T: Clone> {
1173    /// The floor of the exponential histogram.
1174    pub floor: T,
1175
1176    /// The initial step of the exponential histogram.
1177    pub initial_step: T,
1178
1179    /// The step multiplier of the exponential histogram.
1180    pub step_multiplier: T,
1181
1182    /// The number of buckets that the exponential histogram can have. This doesn't include the
1183    /// overflow and underflow buckets.
1184    pub buckets: usize,
1185}
1186
1187/// The parameters of a linear histogram.
1188#[derive(Clone)]
1189pub struct LinearHistogramParams<T: Clone> {
1190    /// The floor of the linear histogram.
1191    pub floor: T,
1192
1193    /// The step size of the linear histogram.
1194    pub step_size: T,
1195
1196    /// The number of buckets that the linear histogram can have. This doesn't include the overflow
1197    /// and underflow buckets.
1198    pub buckets: usize,
1199}
1200
1201/// A type which can function as a "view" into a diagnostics hierarchy, optionally allocating a new
1202/// instance to service a request.
1203pub trait DiagnosticsHierarchyGetter<K: Clone> {
1204    fn get_diagnostics_hierarchy<'a>(
1205        &'a self,
1206    ) -> impl std::future::Future<Output = Cow<'_, DiagnosticsHierarchy<K>>>
1207    where
1208        K: 'a;
1209}
1210
1211impl<K: Clone> DiagnosticsHierarchyGetter<K> for DiagnosticsHierarchy<K> {
1212    async fn get_diagnostics_hierarchy<'a>(&'a self) -> Cow<'_, DiagnosticsHierarchy<K>>
1213    where
1214        K: 'a,
1215    {
1216        Cow::Borrowed(self)
1217    }
1218}
1219
1220#[cfg(test)]
1221mod tests {
1222    use super::*;
1223    use crate::testing::CondensableOnDemand;
1224    use test_case::test_case;
1225
1226    use assert_matches::assert_matches;
1227    use selectors::VerboseError;
1228    use std::sync::Arc;
1229
1230    fn validate_hierarchy_iteration(
1231        mut results_vec: Vec<(Vec<String>, Option<Property>)>,
1232        test_hierarchy: DiagnosticsHierarchy,
1233    ) {
1234        let expected_num_entries = results_vec.len();
1235        let mut num_entries = 0;
1236        for (key, val) in test_hierarchy.property_iter() {
1237            num_entries += 1;
1238            let (expected_key, expected_property) = results_vec.pop().unwrap();
1239            assert_eq!(key.to_vec().join("/"), expected_key.to_vec().join("/"));
1240            assert_eq!(val, expected_property.as_ref());
1241        }
1242
1243        assert_eq!(num_entries, expected_num_entries);
1244    }
1245
1246    fn validate_hierarchy_error_iteration(
1247        mut results_vec: Vec<(Vec<String>, Option<MissingValue>)>,
1248        test_hierarchy: DiagnosticsHierarchy,
1249    ) {
1250        let expected_num_entries = results_vec.len();
1251        let mut num_entries = 0;
1252        for (key, reason) in test_hierarchy.error_iter() {
1253            num_entries += 1;
1254            let (expected_key, expected_reason) = results_vec.pop().unwrap();
1255            assert_eq!(reason, expected_reason.as_ref());
1256            assert_eq!(key.to_vec().join("/"), expected_key.to_vec().join("/"));
1257        }
1258
1259        assert_eq!(num_entries, expected_num_entries);
1260    }
1261
1262    #[fuchsia::test]
1263    fn test_diagnostics_hierarchy_property_iteration() {
1264        let double_array_data = vec![-1.2, 2.3, 3.4, 4.5, -5.6];
1265        let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
1266        let string_data = chars.iter().cycle().take(6000).collect::<String>();
1267        let bytes_data = (0u8..=9u8).cycle().take(5000).collect::<Vec<u8>>();
1268
1269        let test_hierarchy = DiagnosticsHierarchy::new(
1270            "root".to_string(),
1271            vec![
1272                Property::Int("int-root".to_string(), 3),
1273                Property::DoubleArray(
1274                    "property-double-array".to_string(),
1275                    ArrayContent::Values(double_array_data.clone()),
1276                ),
1277            ],
1278            vec![DiagnosticsHierarchy::new(
1279                "child-1".to_string(),
1280                vec![
1281                    Property::Uint("property-uint".to_string(), 10),
1282                    Property::Double("property-double".to_string(), -3.4),
1283                    Property::String("property-string".to_string(), string_data.clone()),
1284                    Property::IntArray(
1285                        "property-int-array".to_string(),
1286                        ArrayContent::new(vec![1, 2, 1, 1, 1, 1, 1], ArrayFormat::LinearHistogram)
1287                            .unwrap(),
1288                    ),
1289                ],
1290                vec![DiagnosticsHierarchy::new(
1291                    "child-1-1".to_string(),
1292                    vec![
1293                        Property::Int("property-int".to_string(), -9),
1294                        Property::Bytes("property-bytes".to_string(), bytes_data.clone()),
1295                        Property::UintArray(
1296                            "property-uint-array".to_string(),
1297                            ArrayContent::new(
1298                                vec![1, 1, 2, 0, 1, 1, 2, 0, 0],
1299                                ArrayFormat::ExponentialHistogram,
1300                            )
1301                            .unwrap(),
1302                        ),
1303                    ],
1304                    vec![],
1305                )],
1306            )],
1307        );
1308
1309        let results_vec = vec![
1310            (
1311                vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1312                Some(Property::UintArray(
1313                    "property-uint-array".to_string(),
1314                    ArrayContent::new(
1315                        vec![1, 1, 2, 0, 1, 1, 2, 0, 0],
1316                        ArrayFormat::ExponentialHistogram,
1317                    )
1318                    .unwrap(),
1319                )),
1320            ),
1321            (
1322                vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1323                Some(Property::Bytes("property-bytes".to_string(), bytes_data)),
1324            ),
1325            (
1326                vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1327                Some(Property::Int("property-int".to_string(), -9)),
1328            ),
1329            (
1330                vec!["root".to_string(), "child-1".to_string()],
1331                Some(Property::IntArray(
1332                    "property-int-array".to_string(),
1333                    ArrayContent::new(vec![1, 2, 1, 1, 1, 1, 1], ArrayFormat::LinearHistogram)
1334                        .unwrap(),
1335                )),
1336            ),
1337            (
1338                vec!["root".to_string(), "child-1".to_string()],
1339                Some(Property::String("property-string".to_string(), string_data)),
1340            ),
1341            (
1342                vec!["root".to_string(), "child-1".to_string()],
1343                Some(Property::Double("property-double".to_string(), -3.4)),
1344            ),
1345            (
1346                vec!["root".to_string(), "child-1".to_string()],
1347                Some(Property::Uint("property-uint".to_string(), 10)),
1348            ),
1349            (
1350                vec!["root".to_string()],
1351                Some(Property::DoubleArray(
1352                    "property-double-array".to_string(),
1353                    ArrayContent::Values(double_array_data),
1354                )),
1355            ),
1356            (vec!["root".to_string()], Some(Property::Int("int-root".to_string(), 3))),
1357        ];
1358
1359        validate_hierarchy_iteration(results_vec, test_hierarchy);
1360    }
1361
1362    #[fuchsia::test]
1363    fn test_diagnostics_hierarchy_error_iteration() {
1364        let mut test_hierarchy = DiagnosticsHierarchy::new(
1365            "root".to_string(),
1366            vec![],
1367            vec![
1368                DiagnosticsHierarchy::new(
1369                    "child-1".to_string(),
1370                    vec![],
1371                    vec![DiagnosticsHierarchy::new("child-1-1".to_string(), vec![], vec![])],
1372                ),
1373                DiagnosticsHierarchy::new("child-2".to_string(), vec![], vec![]),
1374            ],
1375        );
1376
1377        test_hierarchy.add_missing(MissingValueReason::LinkInvalid, "root".to_string());
1378        test_hierarchy.children[0]
1379            .add_missing(MissingValueReason::LinkNeverExpanded, "child-1".to_string());
1380        test_hierarchy.children[0].children[0]
1381            .add_missing(MissingValueReason::Timeout, "child-1-1".to_string());
1382
1383        let results_vec = vec![
1384            (
1385                vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1386                Some(MissingValue {
1387                    reason: MissingValueReason::Timeout,
1388                    name: "child-1-1".to_string(),
1389                }),
1390            ),
1391            (
1392                vec!["root".to_string(), "child-1".to_string()],
1393                Some(MissingValue {
1394                    reason: MissingValueReason::LinkNeverExpanded,
1395                    name: "child-1".to_string(),
1396                }),
1397            ),
1398            (vec!["root".to_string(), "child-2".to_string()], None),
1399            (
1400                vec!["root".to_string()],
1401                Some(MissingValue {
1402                    reason: MissingValueReason::LinkInvalid,
1403                    name: "root".to_string(),
1404                }),
1405            ),
1406        ];
1407
1408        validate_hierarchy_error_iteration(results_vec, test_hierarchy);
1409    }
1410
1411    #[fuchsia::test]
1412    fn test_getters() {
1413        let a_prop = Property::Int("a".to_string(), 1);
1414        let b_prop = Property::Uint("b".to_string(), 2);
1415        let child2 = DiagnosticsHierarchy::new("child2".to_string(), vec![], vec![]);
1416        let child = DiagnosticsHierarchy::new(
1417            "child".to_string(),
1418            vec![b_prop.clone()],
1419            vec![child2.clone()],
1420        );
1421        let mut hierarchy = DiagnosticsHierarchy::new(
1422            "root".to_string(),
1423            vec![a_prop.clone()],
1424            vec![child.clone()],
1425        );
1426        assert_matches!(hierarchy.get_child("child"), Some(node) if *node == child);
1427        assert_matches!(hierarchy.get_child_mut("child"), Some(node) if *node == child);
1428        assert_matches!(hierarchy.get_child_by_path(&["child", "child2"]),
1429                        Some(node) if *node == child2);
1430        assert_matches!(hierarchy.get_child_by_path_mut(&["child", "child2"]),
1431                        Some(node) if *node == child2);
1432        assert_matches!(hierarchy.get_property("a"), Some(prop) if *prop == a_prop);
1433        assert_matches!(hierarchy.get_property_by_path(&["child", "b"]),
1434                        Some(prop) if *prop == b_prop);
1435    }
1436
1437    #[fuchsia::test]
1438    fn test_edge_case_hierarchy_iteration() {
1439        let root_only_with_one_property_hierarchy = DiagnosticsHierarchy::new(
1440            "root".to_string(),
1441            vec![Property::Int("property-int".to_string(), -9)],
1442            vec![],
1443        );
1444
1445        let results_vec =
1446            vec![(vec!["root".to_string()], Some(Property::Int("property-int".to_string(), -9)))];
1447
1448        validate_hierarchy_iteration(results_vec, root_only_with_one_property_hierarchy);
1449
1450        let empty_hierarchy = DiagnosticsHierarchy::new("root".to_string(), vec![], vec![]);
1451
1452        let results_vec = vec![(vec!["root".to_string()], None)];
1453
1454        validate_hierarchy_iteration(results_vec, empty_hierarchy);
1455
1456        let empty_root_populated_child = DiagnosticsHierarchy::new(
1457            "root",
1458            vec![],
1459            vec![DiagnosticsHierarchy::new(
1460                "foo",
1461                vec![Property::Int("11".to_string(), -4)],
1462                vec![],
1463            )],
1464        );
1465
1466        let results_vec = vec![
1467            (
1468                vec!["root".to_string(), "foo".to_string()],
1469                Some(Property::Int("11".to_string(), -4)),
1470            ),
1471            (vec!["root".to_string()], None),
1472        ];
1473
1474        validate_hierarchy_iteration(results_vec, empty_root_populated_child);
1475
1476        let empty_root_empty_child = DiagnosticsHierarchy::new(
1477            "root",
1478            vec![],
1479            vec![DiagnosticsHierarchy::new("foo", vec![], vec![])],
1480        );
1481
1482        let results_vec = vec![
1483            (vec!["root".to_string(), "foo".to_string()], None),
1484            (vec!["root".to_string()], None),
1485        ];
1486
1487        validate_hierarchy_iteration(results_vec, empty_root_empty_child);
1488    }
1489
1490    #[fuchsia::test]
1491    fn array_value() {
1492        let values = vec![1, 2, 5, 7, 9, 11, 13];
1493        let array = ArrayContent::<u64>::new(values.clone(), ArrayFormat::Default);
1494        assert_matches!(array, Ok(ArrayContent::Values(vals)) if vals == values);
1495    }
1496
1497    #[fuchsia::test]
1498    fn linear_histogram_array_value() {
1499        let values = vec![1, 2, 5, 7, 9, 11, 13];
1500        let array = ArrayContent::<i64>::new(values, ArrayFormat::LinearHistogram);
1501        assert_matches!(array, Ok(ArrayContent::LinearHistogram(hist))
1502            if hist == LinearHistogram {
1503                floor: 1,
1504                step: 2,
1505                counts: vec![5, 7, 9, 11, 13],
1506                indexes: None,
1507                size: 5,
1508            }
1509        );
1510    }
1511
1512    #[fuchsia::test]
1513    fn exponential_histogram_array_value() {
1514        let values = vec![1.0, 2.0, 5.0, 7.0, 9.0, 11.0, 15.0];
1515        let array = ArrayContent::<f64>::new(values, ArrayFormat::ExponentialHistogram);
1516        assert_matches!(array, Ok(ArrayContent::ExponentialHistogram(hist))
1517            if hist == ExponentialHistogram {
1518                floor: 1.0,
1519                initial_step: 2.0,
1520                step_multiplier: 5.0,
1521                counts: vec![7.0, 9.0, 11.0, 15.0],
1522                indexes: None,
1523                size: 4,
1524            }
1525        );
1526    }
1527
1528    #[fuchsia::test]
1529    fn deserialize_linear_int_histogram() -> Result<(), serde_json::Error> {
1530        let json = r#"{
1531            "root": {
1532                "histogram": {
1533                    "floor": -2,
1534                    "step": 3,
1535                    "counts": [4, 5, 6],
1536                    "size": 3
1537                }
1538            }
1539        }"#;
1540        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1541        let expected = DiagnosticsHierarchy::new(
1542            "root".to_string(),
1543            vec![Property::IntArray(
1544                "histogram".to_string(),
1545                ArrayContent::new(vec![-2, 3, 4, 5, 6], ArrayFormat::LinearHistogram).unwrap(),
1546            )],
1547            vec![],
1548        );
1549        assert_eq!(parsed, expected);
1550        Ok(())
1551    }
1552
1553    #[fuchsia::test]
1554    fn deserialize_exponential_int_histogram() -> Result<(), serde_json::Error> {
1555        let json = r#"{
1556            "root": {
1557                "histogram": {
1558                    "floor": 1,
1559                    "initial_step": 3,
1560                    "step_multiplier": 2,
1561                    "counts": [4, 5, 6],
1562                    "size": 3
1563                }
1564            }
1565        }"#;
1566        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1567        let expected = DiagnosticsHierarchy::new(
1568            "root".to_string(),
1569            vec![Property::IntArray(
1570                "histogram".to_string(),
1571                ArrayContent::new(vec![1, 3, 2, 4, 5, 6], ArrayFormat::ExponentialHistogram)
1572                    .unwrap(),
1573            )],
1574            vec![],
1575        );
1576        assert_eq!(parsed, expected);
1577        Ok(())
1578    }
1579
1580    #[fuchsia::test]
1581    fn deserialize_linear_uint_histogram() -> Result<(), serde_json::Error> {
1582        let json = r#"{
1583            "root": {
1584                "histogram": {
1585                    "floor": 2,
1586                    "step": 3,
1587                    "counts": [4, 9223372036854775808, 6],
1588                    "size": 3
1589                }
1590            }
1591        }"#;
1592        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1593        let expected = DiagnosticsHierarchy::new(
1594            "root".to_string(),
1595            vec![Property::UintArray(
1596                "histogram".to_string(),
1597                ArrayContent::new(
1598                    vec![2, 3, 4, 9_223_372_036_854_775_808, 6],
1599                    ArrayFormat::LinearHistogram,
1600                )
1601                .unwrap(),
1602            )],
1603            vec![],
1604        );
1605        assert_eq!(parsed, expected);
1606        Ok(())
1607    }
1608
1609    #[fuchsia::test]
1610    fn deserialize_linear_double_histogram() -> Result<(), serde_json::Error> {
1611        let json = r#"{
1612            "root": {
1613                "histogram": {
1614                    "floor": 2.0,
1615                    "step": 3.0,
1616                    "counts": [4.0, 5.0, 6.0],
1617                    "size": 3
1618                }
1619            }
1620        }"#;
1621        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1622        let expected = DiagnosticsHierarchy::new(
1623            "root".to_string(),
1624            vec![Property::DoubleArray(
1625                "histogram".to_string(),
1626                ArrayContent::new(vec![2.0, 3.0, 4.0, 5.0, 6.0], ArrayFormat::LinearHistogram)
1627                    .unwrap(),
1628            )],
1629            vec![],
1630        );
1631        assert_eq!(parsed, expected);
1632        Ok(())
1633    }
1634
1635    #[fuchsia::test]
1636    fn deserialize_sparse_histogram() -> Result<(), serde_json::Error> {
1637        let json = r#"{
1638            "root": {
1639                "histogram": {
1640                    "floor": 2,
1641                    "step": 3,
1642                    "counts": [4, 5, 6],
1643                    "indexes": [1, 2, 4],
1644                    "size": 8
1645                }
1646            }
1647        }"#;
1648        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1649
1650        let mut histogram =
1651            ArrayContent::new(vec![2, 3, 0, 4, 5, 0, 6, 0, 0, 0], ArrayFormat::LinearHistogram)
1652                .unwrap();
1653        histogram.condense_histogram();
1654        let expected = DiagnosticsHierarchy::new(
1655            "root".to_string(),
1656            vec![Property::IntArray("histogram".to_string(), histogram)],
1657            vec![],
1658        );
1659        assert_eq!(parsed, expected);
1660        Ok(())
1661    }
1662
1663    // If a struct can't be parsed as a valid histogram, it will be created as a Node. So if
1664    // there's a node "histogram" (as opposed to a property "histogram") then it didn't parse
1665    // as a histogram.
1666
1667    #[fuchsia::test]
1668    fn reject_histogram_incompatible_values() -> Result<(), serde_json::Error> {
1669        let json = r#"{
1670            "root": {
1671                "histogram": {
1672                    "floor": -2,
1673                    "step": 3,
1674                    "counts": [4, 9223372036854775808, 6],
1675                    "size": 3
1676                }
1677            }
1678        }"#;
1679        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1680        assert_eq!(parsed.children.len(), 1);
1681        assert_eq!(&parsed.children[0].name, "histogram");
1682        Ok(())
1683    }
1684
1685    #[fuchsia::test]
1686    fn reject_histogram_bad_sparse_list() -> Result<(), serde_json::Error> {
1687        let json = r#"{
1688            "root": {
1689                "histogram": {
1690                    "floor": -2,
1691                    "step": 3,
1692                    "counts": [4, 5, 6],
1693                    "indexes": [0, 1, 2, 3],
1694                    "size": 8
1695                }
1696            }
1697        }"#;
1698        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1699        assert_eq!(parsed.children.len(), 1);
1700        assert_eq!(&parsed.children[0].name, "histogram");
1701        Ok(())
1702    }
1703
1704    #[fuchsia::test]
1705    fn reject_histogram_bad_index() -> Result<(), serde_json::Error> {
1706        let json = r#"{
1707            "root": {
1708                "histogram": {
1709                    "floor": -2,
1710                    "step": 3,
1711                    "counts": [4, 5, 6],
1712                    "indexes": [0, 1, 4],
1713                    "size": 4
1714                }
1715            }
1716        }"#;
1717        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1718        assert_eq!(parsed.children.len(), 1);
1719        assert_eq!(&parsed.children[0].name, "histogram");
1720        Ok(())
1721    }
1722
1723    #[fuchsia::test]
1724    fn reject_histogram_wrong_field() -> Result<(), serde_json::Error> {
1725        let json = r#"{
1726            "root": {
1727                "histogram": {
1728                    "floor": 2,
1729                    "step": 3,
1730                    "counts": [4, 5, 6],
1731                    "incorrect": [0, 1, 3],
1732                    "size": 4
1733                }
1734            }
1735        }"#;
1736        let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1737        assert_eq!(parsed.children.len(), 1);
1738        assert_eq!(&parsed.children[0].name, "histogram");
1739        Ok(())
1740    }
1741
1742    #[fuchsia::test]
1743    fn exponential_histogram() {
1744        let values = vec![0, 2, 4, 0, 1, 2, 3, 4, 5];
1745        let array = ArrayContent::new(values, ArrayFormat::ExponentialHistogram);
1746        assert_matches!(array, Ok(ArrayContent::ExponentialHistogram(hist))
1747            if hist == ExponentialHistogram {
1748                floor: 0,
1749                initial_step: 2,
1750                step_multiplier: 4,
1751                counts: vec![0, 1, 2, 3, 4, 5],
1752                indexes: None,
1753                size: 6,
1754            }
1755        );
1756    }
1757
1758    #[fuchsia::test]
1759    fn add_to_hierarchy() {
1760        let mut hierarchy = DiagnosticsHierarchy::new_root();
1761        let prop_1 = Property::String("x".to_string(), "foo".to_string());
1762        let path_1 = vec!["root", "one"];
1763        let prop_2 = Property::Uint("c".to_string(), 3);
1764        let path_2 = vec!["root", "two"];
1765        let prop_2_prime = Property::Int("z".to_string(), -4);
1766        hierarchy.add_property_at_path(&path_1, prop_1.clone());
1767        hierarchy.add_property_at_path(&path_2.clone(), prop_2.clone());
1768        hierarchy.add_property_at_path(&path_2, prop_2_prime.clone());
1769
1770        assert_eq!(
1771            hierarchy,
1772            DiagnosticsHierarchy {
1773                name: "root".to_string(),
1774                children: vec![
1775                    DiagnosticsHierarchy {
1776                        name: "one".to_string(),
1777                        properties: vec![prop_1],
1778                        children: vec![],
1779                        missing: vec![],
1780                    },
1781                    DiagnosticsHierarchy {
1782                        name: "two".to_string(),
1783                        properties: vec![prop_2, prop_2_prime],
1784                        children: vec![],
1785                        missing: vec![],
1786                    }
1787                ],
1788                properties: vec![],
1789                missing: vec![],
1790            }
1791        );
1792    }
1793
1794    #[fuchsia::test]
1795    fn string_lists() {
1796        let mut hierarchy = DiagnosticsHierarchy::new_root();
1797        let prop_1 =
1798            Property::StringList("x".to_string(), vec!["foo".to_string(), "bar".to_string()]);
1799        let path_1 = vec!["root", "one"];
1800        hierarchy.add_property_at_path(&path_1, prop_1.clone());
1801
1802        assert_eq!(
1803            hierarchy,
1804            DiagnosticsHierarchy {
1805                name: "root".to_string(),
1806                children: vec![DiagnosticsHierarchy {
1807                    name: "one".to_string(),
1808                    properties: vec![prop_1],
1809                    children: vec![],
1810                    missing: vec![],
1811                },],
1812                properties: vec![],
1813                missing: vec![],
1814            }
1815        );
1816    }
1817
1818    #[fuchsia::test]
1819    // TODO(https://fxbug.dev/42169733): delete the below
1820    #[cfg_attr(feature = "variant_asan", ignore)]
1821    #[cfg_attr(feature = "variant_hwasan", ignore)]
1822    #[should_panic]
1823    // Empty paths are meaningless on insertion and break the method invariant.
1824    fn no_empty_paths_allowed() {
1825        let mut hierarchy = DiagnosticsHierarchy::<String>::new_root();
1826        let path_1: Vec<&String> = vec![];
1827        hierarchy.get_or_add_node(&path_1);
1828    }
1829
1830    #[fuchsia::test]
1831    #[should_panic]
1832    // Paths provided to add must begin at the node we're calling
1833    // add() on.
1834    fn path_must_start_at_self() {
1835        let mut hierarchy = DiagnosticsHierarchy::<String>::new_root();
1836        let path_1 = vec!["not_root", "a"];
1837        hierarchy.get_or_add_node(&path_1);
1838    }
1839
1840    #[fuchsia::test]
1841    fn sort_hierarchy() {
1842        let mut hierarchy = DiagnosticsHierarchy::new(
1843            "root",
1844            vec![
1845                Property::String("x".to_string(), "foo".to_string()),
1846                Property::Uint("c".to_string(), 3),
1847                Property::Int("z".to_string(), -4),
1848            ],
1849            vec![
1850                DiagnosticsHierarchy::new(
1851                    "foo",
1852                    vec![
1853                        Property::Int("11".to_string(), -4),
1854                        Property::Bytes("123".to_string(), "foo".bytes().collect()),
1855                        Property::Double("0".to_string(), 8.1),
1856                    ],
1857                    vec![],
1858                ),
1859                DiagnosticsHierarchy::new("bar", vec![], vec![]),
1860            ],
1861        );
1862
1863        hierarchy.sort();
1864
1865        let sorted_hierarchy = DiagnosticsHierarchy::new(
1866            "root",
1867            vec![
1868                Property::Uint("c".to_string(), 3),
1869                Property::String("x".to_string(), "foo".to_string()),
1870                Property::Int("z".to_string(), -4),
1871            ],
1872            vec![
1873                DiagnosticsHierarchy::new("bar", vec![], vec![]),
1874                DiagnosticsHierarchy::new(
1875                    "foo",
1876                    vec![
1877                        Property::Double("0".to_string(), 8.1),
1878                        Property::Int("11".to_string(), -4),
1879                        Property::Bytes("123".to_string(), "foo".bytes().collect()),
1880                    ],
1881                    vec![],
1882                ),
1883            ],
1884        );
1885        assert_eq!(sorted_hierarchy, hierarchy);
1886    }
1887
1888    fn parse_selectors_and_filter_hierarchy(
1889        hierarchy: DiagnosticsHierarchy,
1890        test_selectors: Vec<&str>,
1891    ) -> Option<DiagnosticsHierarchy> {
1892        let parsed_test_selectors = test_selectors
1893            .into_iter()
1894            .map(|selector_string| {
1895                Arc::new(
1896                    selectors::parse_selector::<VerboseError>(selector_string)
1897                        .expect("All test selectors are valid and parsable."),
1898                )
1899            })
1900            .collect::<Vec<Arc<Selector>>>();
1901
1902        let hierarchy_matcher: HierarchyMatcher = parsed_test_selectors.try_into().unwrap();
1903
1904        filter_hierarchy(hierarchy, &hierarchy_matcher).map(|mut hierarchy| {
1905            hierarchy.sort();
1906            hierarchy
1907        })
1908    }
1909
1910    fn get_test_hierarchy() -> DiagnosticsHierarchy {
1911        DiagnosticsHierarchy::new(
1912            "root",
1913            vec![
1914                Property::String("x".to_string(), "foo".to_string()),
1915                Property::Uint("c".to_string(), 3),
1916                Property::Int("z".to_string(), -4),
1917            ],
1918            vec![
1919                make_foo(),
1920                DiagnosticsHierarchy::new(
1921                    "bar",
1922                    vec![Property::Int("12".to_string(), -4)],
1923                    vec![DiagnosticsHierarchy::new(
1924                        "zed",
1925                        vec![Property::Int("13/:".to_string(), -4)],
1926                        vec![],
1927                    )],
1928                ),
1929            ],
1930        )
1931    }
1932
1933    fn make_all_foo_props() -> Vec<Property> {
1934        vec![
1935            Property::Int("11".to_string(), -4),
1936            Property::Bytes("123".to_string(), b"foo".to_vec()),
1937            Property::Double("0".to_string(), 8.1),
1938        ]
1939    }
1940
1941    fn make_zed() -> Vec<DiagnosticsHierarchy> {
1942        vec![DiagnosticsHierarchy::new("zed", vec![Property::Int("13".to_string(), -4)], vec![])]
1943    }
1944
1945    fn make_foo() -> DiagnosticsHierarchy {
1946        DiagnosticsHierarchy::new("foo", make_all_foo_props(), make_zed())
1947    }
1948
1949    #[fuchsia::test]
1950    fn test_filter_hierarchy() {
1951        let test_selectors = vec!["*:root/foo:11", "*:root:z", r#"*:root/bar/zed:13\/\:"#];
1952
1953        assert_eq!(
1954            parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
1955            Some(DiagnosticsHierarchy::new(
1956                "root",
1957                vec![Property::Int("z".to_string(), -4),],
1958                vec![
1959                    DiagnosticsHierarchy::new(
1960                        "bar",
1961                        vec![],
1962                        vec![DiagnosticsHierarchy::new(
1963                            "zed",
1964                            vec![Property::Int("13/:".to_string(), -4)],
1965                            vec![],
1966                        )],
1967                    ),
1968                    DiagnosticsHierarchy::new(
1969                        "foo",
1970                        vec![Property::Int("11".to_string(), -4),],
1971                        vec![],
1972                    )
1973                ],
1974            ))
1975        );
1976
1977        let test_selectors = vec!["*:root"];
1978        let mut sorted_expected = get_test_hierarchy();
1979        sorted_expected.sort();
1980        assert_eq!(
1981            parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
1982            Some(sorted_expected)
1983        );
1984    }
1985
1986    #[fuchsia::test]
1987    fn test_filter_does_not_include_empty_node() {
1988        let test_selectors = vec!["*:root/foo:blorg"];
1989
1990        assert_eq!(
1991            parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
1992            None,
1993        );
1994    }
1995
1996    #[fuchsia::test]
1997    fn test_filter_empty_hierarchy() {
1998        let test_selectors = vec!["*:root"];
1999
2000        assert_eq!(
2001            parse_selectors_and_filter_hierarchy(
2002                DiagnosticsHierarchy::new("root", vec![], vec![]),
2003                test_selectors
2004            ),
2005            Some(DiagnosticsHierarchy::new("root", vec![], vec![])),
2006        );
2007    }
2008
2009    #[fuchsia::test]
2010    fn test_full_filtering() {
2011        // If we select a non-existent root, then we return a fully filtered hierarchy.
2012        let test_selectors = vec!["*:non-existent-root"];
2013        assert_eq!(
2014            parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2015            None,
2016        );
2017
2018        // If we select a non-existent child of the root, then we return a fully filtered hierarchy.
2019        let test_selectors = vec!["*:root/i-dont-exist:foo"];
2020        assert_eq!(
2021            parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2022            None,
2023        );
2024
2025        // Even if the root exists, but we don't include any property, we consider the hierarchy
2026        // fully filtered. This is aligned with the previous case.
2027        let test_selectors = vec!["*:root:i-dont-exist"];
2028        assert_eq!(
2029            parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2030            None,
2031        );
2032    }
2033
2034    #[fuchsia::test]
2035    fn test_subtree_selection_includes_empty_nodes() {
2036        let test_selectors = vec!["*:root"];
2037        let mut empty_hierarchy = DiagnosticsHierarchy::new(
2038            "root",
2039            vec![],
2040            vec![
2041                DiagnosticsHierarchy::new(
2042                    "foo",
2043                    vec![],
2044                    vec![DiagnosticsHierarchy::new("zed", vec![], vec![])],
2045                ),
2046                DiagnosticsHierarchy::new(
2047                    "bar",
2048                    vec![],
2049                    vec![DiagnosticsHierarchy::new("zed", vec![], vec![])],
2050                ),
2051            ],
2052        );
2053
2054        empty_hierarchy.sort();
2055
2056        assert_eq!(
2057            parse_selectors_and_filter_hierarchy(empty_hierarchy.clone(), test_selectors),
2058            Some(empty_hierarchy)
2059        );
2060    }
2061
2062    #[fuchsia::test]
2063    fn test_empty_tree_filtering() {
2064        // Subtree selection on the empty tree should produce the empty tree.
2065        let mut empty_hierarchy = DiagnosticsHierarchy::new("root", vec![], vec![]);
2066        empty_hierarchy.sort();
2067
2068        let subtree_selector = vec!["*:root"];
2069        assert_eq!(
2070            parse_selectors_and_filter_hierarchy(empty_hierarchy.clone(), subtree_selector),
2071            Some(empty_hierarchy.clone())
2072        );
2073
2074        // Selecting a property on the root, even if it doesn't exist, should produce nothing.
2075        let fake_property_selector = vec!["*:root:blorp"];
2076        assert_eq!(
2077            parse_selectors_and_filter_hierarchy(empty_hierarchy.clone(), fake_property_selector),
2078            None,
2079        );
2080    }
2081
2082    #[test_case(vec![Property::Int("11".to_string(), -4)], "root/foo:11" ; "specific_property")]
2083    #[test_case(make_all_foo_props(), "root/foo:*" ; "many_properties")]
2084    #[test_case(vec![], "root/foo:none" ; "property_not_there")]
2085    #[fuchsia::test]
2086    fn test_select_from_hierarchy_property_selectors(expected: Vec<Property>, tree_selector: &str) {
2087        let hierarchy = get_test_hierarchy();
2088        let parsed_selector =
2089            selectors::parse_selector::<VerboseError>(&format!("*:{tree_selector}"))
2090                .expect("All test selectors are valid and parsable.");
2091        let Ok(SelectResult::Properties(mut property_entry_vec)) =
2092            select_from_hierarchy(&hierarchy, &parsed_selector)
2093        else {
2094            panic!("must be properties");
2095        };
2096
2097        property_entry_vec.sort_by(|p1, p2| p1.name().cmp(p2.name()));
2098        let mut expected = expected.iter().map(Borrow::borrow).collect::<Vec<_>>();
2099        expected.sort_by(|p1, p2| p1.name().cmp(p2.name()));
2100
2101        assert_eq!(property_entry_vec, expected);
2102    }
2103
2104    #[test_case(vec![], "root/none" ; "node_not_there")]
2105    #[test_case(make_zed(), "root/foo/zed" ; "properties_only")]
2106    #[test_case(vec![make_foo()], "root/foo" ; "nodes_and_properties")]
2107    #[test_case(vec![get_test_hierarchy()], "root" ; "select_root")]
2108    #[fuchsia::test]
2109    fn test_select_from_hierarchy_tree_selectors(
2110        expected: Vec<DiagnosticsHierarchy>,
2111        tree_selector: &str,
2112    ) {
2113        let hierarchy = get_test_hierarchy();
2114        let parsed_selector =
2115            selectors::parse_selector::<VerboseError>(&format!("*:{tree_selector}"))
2116                .expect("All test selectors are valid and parsable.");
2117        let Ok(SelectResult::Nodes(node_vec)) = select_from_hierarchy(&hierarchy, &parsed_selector)
2118        else {
2119            panic!("must be nodes");
2120        };
2121
2122        let expected = expected.iter().map(Borrow::borrow).collect::<Vec<_>>();
2123
2124        assert_eq!(node_vec, expected);
2125    }
2126
2127    #[fuchsia::test]
2128    fn sort_numerical_value() {
2129        let mut diagnostics_hierarchy = DiagnosticsHierarchy::new(
2130            "root",
2131            vec![
2132                Property::Double("2".to_string(), 2.3),
2133                Property::Int("0".to_string(), -4),
2134                Property::Uint("10".to_string(), 3),
2135                Property::String("1".to_string(), "test".to_string()),
2136            ],
2137            vec![
2138                DiagnosticsHierarchy::new("123", vec![], vec![]),
2139                DiagnosticsHierarchy::new("34", vec![], vec![]),
2140                DiagnosticsHierarchy::new("4", vec![], vec![]),
2141                DiagnosticsHierarchy::new("023", vec![], vec![]),
2142                DiagnosticsHierarchy::new("12", vec![], vec![]),
2143                DiagnosticsHierarchy::new("1", vec![], vec![]),
2144            ],
2145        );
2146        diagnostics_hierarchy.sort();
2147        assert_eq!(
2148            diagnostics_hierarchy,
2149            DiagnosticsHierarchy::new(
2150                "root",
2151                vec![
2152                    Property::Int("0".to_string(), -4),
2153                    Property::String("1".to_string(), "test".to_string()),
2154                    Property::Double("2".to_string(), 2.3),
2155                    Property::Uint("10".to_string(), 3),
2156                ],
2157                vec![
2158                    DiagnosticsHierarchy::new("1", vec![], vec![]),
2159                    DiagnosticsHierarchy::new("4", vec![], vec![]),
2160                    DiagnosticsHierarchy::new("12", vec![], vec![]),
2161                    DiagnosticsHierarchy::new("023", vec![], vec![]),
2162                    DiagnosticsHierarchy::new("34", vec![], vec![]),
2163                    DiagnosticsHierarchy::new("123", vec![], vec![]),
2164                ]
2165            )
2166        );
2167    }
2168
2169    #[fuchsia::test]
2170    fn filter_hierarchy_doesnt_return_partial_matches() {
2171        let hierarchy = DiagnosticsHierarchy::new(
2172            "root",
2173            vec![],
2174            vec![DiagnosticsHierarchy::new("session_started_at", vec![], vec![])],
2175        );
2176        let test_selectors = vec!["*:root/session_started_at/0"];
2177        assert_eq!(parse_selectors_and_filter_hierarchy(hierarchy, test_selectors), None);
2178    }
2179
2180    #[fuchsia::test]
2181    fn test_filter_tree() {
2182        let test_selectors = vec!["root/foo:11", "root:z", r#"root/bar/zed:13\/\:"#];
2183        let parsed_test_selectors = test_selectors
2184            .into_iter()
2185            .map(|s| {
2186                selectors::parse_tree_selector::<VerboseError>(s)
2187                    .expect("All test selectors are valid and parsable.")
2188            })
2189            .collect::<Vec<_>>();
2190
2191        let result =
2192            filter_tree(get_test_hierarchy(), &parsed_test_selectors).map(|mut hierarchy| {
2193                hierarchy.sort();
2194                hierarchy
2195            });
2196        assert_eq!(
2197            result,
2198            Some(DiagnosticsHierarchy::new(
2199                "root",
2200                vec![Property::Int("z".to_string(), -4),],
2201                vec![
2202                    DiagnosticsHierarchy::new(
2203                        "bar",
2204                        vec![],
2205                        vec![DiagnosticsHierarchy::new(
2206                            "zed",
2207                            vec![Property::Int("13/:".to_string(), -4)],
2208                            vec![],
2209                        )],
2210                    ),
2211                    DiagnosticsHierarchy::new(
2212                        "foo",
2213                        vec![Property::Int("11".to_string(), -4),],
2214                        vec![],
2215                    )
2216                ],
2217            ))
2218        );
2219    }
2220
2221    #[fuchsia::test]
2222    fn test_matcher_from_iterator() {
2223        let matcher = HierarchyMatcher::new(
2224            ["*:root/foo:11", "*:root:z", r#"*:root/bar/zed:13\/\:"#].into_iter().map(|s| {
2225                selectors::parse_selector::<VerboseError>(s)
2226                    .expect("All test selectors are valid and parsable.")
2227            }),
2228        )
2229        .expect("create matcher from iterator of selectors");
2230        let result = filter_hierarchy(get_test_hierarchy(), &matcher).map(|mut hierarchy| {
2231            hierarchy.sort();
2232            hierarchy
2233        });
2234        assert_eq!(
2235            result,
2236            Some(DiagnosticsHierarchy::new(
2237                "root",
2238                vec![Property::Int("z".to_string(), -4),],
2239                vec![
2240                    DiagnosticsHierarchy::new(
2241                        "bar",
2242                        vec![],
2243                        vec![DiagnosticsHierarchy::new(
2244                            "zed",
2245                            vec![Property::Int("13/:".to_string(), -4)],
2246                            vec![],
2247                        )],
2248                    ),
2249                    DiagnosticsHierarchy::new(
2250                        "foo",
2251                        vec![Property::Int("11".to_string(), -4),],
2252                        vec![],
2253                    )
2254                ],
2255            ))
2256        );
2257    }
2258}