Skip to main content

diagnostics_hierarchy/
lib.rs

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