probe_rs_debug/
variable_cache.rs

1use super::*;
2use crate::{stack_frame::StackFrameInfo, unit_info::UnitInfo};
3use gimli::UnitOffset;
4use probe_rs::Error;
5use probe_rs_target::MemoryRange;
6use serde::{Serialize, Serializer};
7use std::{
8    collections::{BTreeMap, btree_map::Entry},
9    ops::Range,
10};
11
12/// VariableCache stores available `Variable`s, and provides methods to create and navigate the parent-child relationships of the Variables.
13#[derive(Debug, Clone, PartialEq)]
14pub struct VariableCache {
15    root_variable_key: ObjectRef,
16
17    variable_hash_map: BTreeMap<ObjectRef, Variable>,
18}
19
20impl Serialize for VariableCache {
21    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
22    where
23        S: Serializer,
24    {
25        use serde::ser::SerializeStruct;
26
27        /// This is a modified version of the [`Variable`] struct, to be used for serialization as a recursive tree node.
28        #[derive(Serialize)]
29        struct VariableTreeNode<'c> {
30            name: &'c VariableName,
31            type_name: &'c VariableType,
32            /// To eliminate noise, we will only show values for base data types and strings.
33            value: String,
34
35            #[serde(skip_serializing_if = "Option::is_none")]
36            source_location: Option<SourceLocation>,
37            /// ONLY If there are children.
38            #[serde(skip_serializing_if = "Vec::is_empty")]
39            children: Vec<VariableTreeNode<'c>>,
40        }
41
42        fn recurse_cache(variable_cache: &VariableCache) -> VariableTreeNode<'_> {
43            let root_node = variable_cache.root_variable();
44
45            VariableTreeNode {
46                name: &root_node.name,
47                type_name: &root_node.type_name,
48                value: root_node.to_string(variable_cache),
49                source_location: root_node.source_location.clone(),
50                children: recurse_variables(variable_cache, root_node.variable_key, None),
51            }
52        }
53
54        /// A helper function to recursively build the variable tree with `VariableTreeNode` entries.
55        fn recurse_variables(
56            variable_cache: &VariableCache,
57            parent_variable_key: ObjectRef,
58            max_children: Option<usize>,
59        ) -> Vec<VariableTreeNode<'_>> {
60            let mut children = variable_cache.get_children(parent_variable_key);
61
62            let mut out = Vec::new();
63
64            loop {
65                if let Some(max_count) = max_children
66                    && out.len() >= max_count
67                {
68                    // Be a bit lenient with the limit, avoid showing "1 more" for a single child.
69                    let remaining = children.clone().count();
70                    if remaining > 1 {
71                        break;
72                    }
73                }
74                let Some(child_variable) = children.next() else {
75                    break;
76                };
77
78                out.push(VariableTreeNode {
79                    name: &child_variable.name,
80                    type_name: &child_variable.type_name,
81                    value: child_variable.to_string(variable_cache),
82                    children: recurse_variables(
83                        variable_cache,
84                        child_variable.variable_key,
85                        // Limit arrays to 50(+1) elements
86                        child_variable.type_name.inner().is_array().then_some(50),
87                    ),
88                    source_location: child_variable.source_location.clone(),
89                });
90            }
91
92            let remaining = children.count();
93            if remaining > 0 {
94                out.push(VariableTreeNode {
95                    name: &VariableName::Artifical,
96                    type_name: &VariableType::Unknown,
97                    value: format!("... and {remaining} more"),
98                    children: Vec::new(),
99                    source_location: None,
100                });
101            }
102
103            out
104        }
105
106        let mut state = serializer.serialize_struct("Variables", 1)?;
107        state.serialize_field("Child Variables", &recurse_cache(self))?;
108        state.end()
109    }
110}
111
112impl VariableCache {
113    fn new(mut variable: Variable) -> Self {
114        let key = get_object_reference();
115
116        variable.variable_key = key;
117
118        VariableCache {
119            root_variable_key: key,
120            variable_hash_map: BTreeMap::from([(key, variable)]),
121        }
122    }
123
124    /// Create a variable cache based on DWARF debug information
125    ///
126    /// The `entries_offset` and `unit_info` values are used to
127    /// extract the variable information from the debug information.
128    ///
129    /// The entries form a tree, only entries below the entry
130    /// at `entries_offset` are considered when filling the cache.
131    pub fn new_dwarf_cache(
132        entries_offset: UnitOffset,
133        name: VariableName,
134        unit_info: &UnitInfo,
135    ) -> Result<Self, DebugError> {
136        let mut static_root_variable = Variable::new(Some(unit_info));
137        static_root_variable.variable_node_type =
138            VariableNodeType::DirectLookup(unit_info.debug_info_offset()?, entries_offset);
139        static_root_variable.name = name;
140
141        Ok(VariableCache::new(static_root_variable))
142    }
143
144    /// Create a cache for static variables.
145    ///
146    /// This will be filled with static variables when `cache_deferred_variables` is called.
147    pub fn new_static_cache() -> Self {
148        let mut static_root_variable = Variable::new(None);
149        static_root_variable.variable_node_type = VariableNodeType::UnitsLookup;
150        static_root_variable.name = VariableName::StaticScopeRoot;
151
152        VariableCache::new(static_root_variable)
153    }
154
155    /// Get the root variable of the cache
156    pub fn root_variable(&self) -> &Variable {
157        &self.variable_hash_map[&self.root_variable_key]
158    }
159
160    /// Returns the number of `Variable`s in the cache.
161    // These caches are constructed with a single root variable, so this should never be empty.
162    pub fn len(&self) -> usize {
163        self.variable_hash_map.len()
164    }
165
166    /// Returns `true` if the cache is empty.
167    pub fn is_empty(&self) -> bool {
168        self.variable_hash_map.is_empty()
169    }
170
171    /// Create a new variable in the cache
172    pub fn create_variable(
173        &mut self,
174        parent_key: ObjectRef,
175        unit_info: Option<&UnitInfo>,
176    ) -> Result<Variable, DebugError> {
177        // Validate that the parent_key exists ...
178        if !self.variable_hash_map.contains_key(&parent_key) {
179            return Err(DebugError::Other(format!(
180                "VariableCache: Attempted to add a new variable with non existent `parent_key`: {parent_key:?}. Please report this as a bug"
181            )));
182        }
183
184        let mut variable_to_add = Variable::new(unit_info);
185        variable_to_add.parent_key = parent_key;
186
187        // The caller is telling us this is definitely a new `Variable`
188        variable_to_add.variable_key = get_object_reference();
189
190        tracing::trace!(
191            "VariableCache: Add Variable: key={:?}, parent={:?}, name={:?}",
192            variable_to_add.variable_key,
193            variable_to_add.parent_key,
194            &variable_to_add.name
195        );
196
197        match self.variable_hash_map.entry(variable_to_add.variable_key) {
198            Entry::Occupied(_) => {
199                return Err(DebugError::Other(format!(
200                    "Attempt to insert a new `Variable`:{:?} with a duplicate cache key: {:?}. Please report this as a bug.",
201                    variable_to_add.name, variable_to_add.variable_key
202                )));
203            }
204            Entry::Vacant(entry) => {
205                entry.insert(variable_to_add.clone());
206            }
207        }
208
209        Ok(variable_to_add)
210    }
211
212    /// Add a variable to the cache
213    ///
214    /// The parent key must exist in the cache, and the variable
215    /// must not have a key assigned yet.
216    pub fn add_variable(
217        &mut self,
218        parent_key: ObjectRef,
219        cache_variable: &mut Variable,
220    ) -> Result<(), DebugError> {
221        // Validate that the parent_key exists ...
222        if !self.variable_hash_map.contains_key(&parent_key) {
223            return Err(DebugError::Other(format!(
224                "VariableCache: Attempted to add a new variable: {} with non existent `parent_key`: {:?}. Please report this as a bug",
225                cache_variable.name, parent_key
226            )));
227        }
228
229        cache_variable.parent_key = parent_key;
230
231        if cache_variable.variable_key != ObjectRef::Invalid {
232            return Err(DebugError::Other(format!(
233                "VariableCache: Attempted to add a new variable: {} with already set key: {:?}. Please report this as a bug",
234                cache_variable.name, cache_variable.variable_key
235            )));
236        }
237
238        // The caller is telling us this is definitely a new `Variable`
239        cache_variable.variable_key = get_object_reference();
240
241        tracing::trace!(
242            "VariableCache: Add Variable: key={:?}, parent={:?}, name={:?}",
243            cache_variable.variable_key,
244            cache_variable.parent_key,
245            cache_variable.name
246        );
247
248        if let Some(old_variable) = self
249            .variable_hash_map
250            .insert(cache_variable.variable_key, cache_variable.clone())
251        {
252            return Err(DebugError::Other(format!(
253                "Attempt to insert a new `Variable`:{:?} with a duplicate cache key: {:?}. Please report this as a bug.",
254                cache_variable.name, old_variable.variable_key
255            )));
256        }
257
258        Ok(())
259    }
260
261    /// Update a variable in the cache
262    ///
263    /// This function does not update the value of the variable.
264    pub fn update_variable(&mut self, cache_variable: &Variable) -> Result<(), DebugError> {
265        // Attempt to update an existing `Variable` in the cache
266        tracing::trace!(
267            "VariableCache: Update Variable, key={:?}, name={:?}",
268            cache_variable.variable_key,
269            &cache_variable.name
270        );
271
272        let Some(prev_entry) = self.variable_hash_map.get_mut(&cache_variable.variable_key) else {
273            return Err(DebugError::Other(format!(
274                "Attempt to update an existing `Variable`:{:?} with a non-existent cache key: {:?}. Please report this as a bug.",
275                cache_variable.name, cache_variable.variable_key
276            )));
277        };
278
279        if cache_variable != prev_entry {
280            tracing::trace!("Updated:  {:?}", cache_variable);
281            tracing::trace!("Previous: {:?}", prev_entry);
282            *prev_entry = cache_variable.clone();
283        }
284
285        Ok(())
286    }
287
288    /// Retrieve a clone of a specific `Variable`, using the `variable_key`.
289    pub fn get_variable_by_key(&self, variable_key: ObjectRef) -> Option<Variable> {
290        self.variable_hash_map.get(&variable_key).cloned()
291    }
292
293    /// Retrieve a clone of a specific `Variable`, using the `name` and `parent_key`.
294    /// If there is more than one, it will be logged (tracing::error!), and only the last will be returned.
295    pub fn get_variable_by_name_and_parent(
296        &self,
297        variable_name: &VariableName,
298        parent_key: ObjectRef,
299    ) -> Option<Variable> {
300        let child_variables = self.variable_hash_map.values().filter(|child_variable| {
301            &child_variable.name == variable_name && child_variable.parent_key == parent_key
302        });
303
304        // Clone the iterator. This is cheap and makes rewinding easier.
305        let mut first_iter = child_variables.clone();
306        let first = first_iter.next();
307        let more = first_iter.next().is_some();
308
309        if more {
310            let (last_index, last) = child_variables.enumerate().last().unwrap();
311            tracing::error!(
312                "Found {} variables with parent_key={:?} and name={}. Please report this as a bug.",
313                last_index + 1,
314                parent_key,
315                variable_name
316            );
317            Some(last.clone())
318        } else {
319            first.cloned()
320        }
321    }
322
323    /// Retrieve a clone of a specific `Variable`, using the `name`.
324    /// If there is more than one, it will be logged (tracing::warn!), and only the first will be returned.
325    /// It is possible for a hierarchy of variables in a cache to have duplicate names under different parents.
326    pub fn get_variable_by_name(&self, variable_name: &VariableName) -> Option<Variable> {
327        let mut child_variables = self
328            .variable_hash_map
329            .values()
330            .filter(|child_variable| child_variable.name.eq(variable_name));
331
332        let first = child_variables.next();
333        let more = child_variables.next().is_some();
334
335        if more {
336            tracing::warn!(
337                "Found {} variables with name={}. Please report this as a bug.",
338                self.variable_hash_map.len(),
339                variable_name
340            );
341        }
342
343        first.cloned()
344    }
345
346    /// Retrieve `clone`d version of all the children of a `Variable`.
347    /// If `parent_key == None`, it will return all the top level variables (no parents) in this cache.
348    pub fn get_children(&self, parent_key: ObjectRef) -> impl Iterator<Item = &Variable> + Clone {
349        self.variable_hash_map
350            .values()
351            .filter(move |child_variable| child_variable.parent_key == parent_key)
352    }
353
354    /// Check if variable has children. If the variable doesn't exist, it will return false.
355    pub fn has_children(&self, parent_variable: &Variable) -> bool {
356        self.get_children(parent_variable.variable_key)
357            .next()
358            .is_some()
359    }
360
361    /// Sometimes DWARF uses intermediate nodes that are not part of the coded variable structure.
362    /// When we encounter them, the children of such intermediate nodes are assigned to the parent of the intermediate node, and we discard the intermediate nodes from the `DebugInfo::VariableCache`
363    ///
364    /// Similarly, while resolving [VariableNodeType::is_deferred()], i.e. 'lazy load' of variables, we need to create intermediate variables that are eliminated here.
365    ///
366    /// NOTE: For all other situations, this function will silently do nothing.
367    pub fn adopt_grand_children(
368        &mut self,
369        parent_variable: &Variable,
370        obsolete_child_variable: &Variable,
371    ) -> Result<(), Error> {
372        if obsolete_child_variable.type_name == VariableType::Unknown
373            || obsolete_child_variable.variable_node_type != VariableNodeType::DoNotRecurse
374        {
375            // Make sure we pass children up, past any intermediate nodes.
376            self.variable_hash_map
377                .values_mut()
378                .filter(|search_variable| {
379                    search_variable.parent_key == obsolete_child_variable.variable_key
380                })
381                .for_each(|grand_child| grand_child.parent_key = parent_variable.variable_key);
382            // Remove the intermediate variable from the cache
383            self.remove_cache_entry(obsolete_child_variable.variable_key)?;
384        }
385        Ok(())
386    }
387
388    /// Removing an entry's children from the `VariableCache` will recursively remove all their children
389    pub fn remove_cache_entry_children(
390        &mut self,
391        parent_variable_key: ObjectRef,
392    ) -> Result<(), Error> {
393        let children = self
394            .variable_hash_map
395            .values()
396            .filter(|child_variable| child_variable.parent_key == parent_variable_key)
397            .cloned()
398            .collect::<Vec<Variable>>();
399
400        for child in children {
401            self.remove_cache_entry(child.variable_key)?;
402        }
403
404        Ok(())
405    }
406    /// Removing an entry from the `VariableCache` will recursively remove all its children
407    pub fn remove_cache_entry(&mut self, variable_key: ObjectRef) -> Result<(), Error> {
408        self.remove_cache_entry_children(variable_key)?;
409        if self.variable_hash_map.remove(&variable_key).is_none() {
410            return Err(Error::Other(format!(
411                "Failed to remove a `VariableCache` entry with key: {variable_key:?}. Please report this as a bug."
412            )));
413        };
414        Ok(())
415    }
416    /// Recursively process the deferred variables in the variable cache,
417    /// and add their children to the cache.
418    /// Enforce a max level, so that we don't recurse infinitely on circular references.
419    pub fn recurse_deferred_variables(
420        &mut self,
421        debug_info: &DebugInfo,
422        memory: &mut dyn MemoryInterface,
423        max_recursion_depth: usize,
424        frame_info: StackFrameInfo<'_>,
425    ) {
426        let mut parent_variable = self.root_variable().clone();
427
428        self.recurse_deferred_variables_internal(
429            debug_info,
430            memory,
431            &mut parent_variable,
432            max_recursion_depth,
433            0,
434            frame_info,
435        )
436    }
437
438    fn recurse_deferred_variables_internal(
439        &mut self,
440        debug_info: &DebugInfo,
441        memory: &mut dyn MemoryInterface,
442        parent_variable: &mut Variable,
443        max_recursion_depth: usize,
444        current_recursion_depth: usize,
445        frame_info: StackFrameInfo<'_>,
446    ) {
447        if current_recursion_depth >= max_recursion_depth {
448            return;
449        }
450
451        if debug_info
452            .cache_deferred_variables(self, memory, parent_variable, frame_info)
453            .is_err()
454        {
455            return;
456        };
457
458        let children: Vec<_> = self
459            .get_children(parent_variable.variable_key)
460            .cloned()
461            .collect();
462
463        for mut child in children {
464            self.recurse_deferred_variables_internal(
465                debug_info,
466                memory,
467                &mut child,
468                max_recursion_depth,
469                current_recursion_depth + 1,
470                frame_info,
471            );
472        }
473    }
474
475    /// Traverse the `VariableCache` and return a Vec of all the memory ranges that are referenced by the variables.
476    /// This is used to determine which memory ranges to read from the target when creating a 'default' [`crate::CoreDump`].
477    pub fn get_discrete_memory_ranges(&self) -> Vec<Range<u64>> {
478        let mut memory_ranges: Vec<Range<u64>> = Vec::new();
479        for variable in self.variable_hash_map.values() {
480            if let Some(mut memory_range) = variable.memory_range() {
481                // This memory may need to be read by 32-bit aligned words, so make sure
482                // the range is aligned to 32 bits.
483                memory_range.align_to_32_bits();
484                if !memory_ranges.contains(&memory_range) {
485                    memory_ranges.push(memory_range);
486                }
487            }
488
489            // The datatype &str is a special case, because it is stores a pointer to the string data,
490            // and the length of the string.
491            if matches!(variable.type_name, VariableType::Struct(ref name) if name == "&str") {
492                let children: Vec<_> = self.get_children(variable.variable_key).collect();
493                if !children.is_empty() {
494                    let string_length = match children.iter().find(|child_variable| {
495                        matches!(child_variable.name, VariableName::Named(ref name) if name == "length")
496                    }) {
497                        Some(string_length) => {
498                            if string_length.is_valid() {
499                                string_length.to_string(self).parse().unwrap_or(0_usize)
500                            } else {
501                                0_usize
502                            }
503                        }
504                        None => 0_usize,
505                    };
506                    let string_location = match children.iter().find(|child_variable| {
507                        matches!(child_variable.name, VariableName::Named(ref name ) if name == "data_ptr")
508                    }) {
509                        Some(location_value) => {
510                            let mut child_variables =
511                                self.get_children(location_value.variable_key);
512                            if let Some(first_child) = child_variables.next() {
513                                first_child
514                                    .memory_location
515                                    .memory_address()
516                                    .unwrap_or(0_u64)
517                            } else {
518                                0_u64
519                            }
520                        }
521                        None => 0_u64,
522                    };
523                    if string_location == 0 || string_length == 0 {
524                        // We don't have enough information to read the string from memory.
525                        // I've never seen an instance of this, but it is theoretically possible.
526                        tracing::warn!(
527                            "Failed to find string location or length for variable: {:?}",
528                            variable
529                        );
530                    } else {
531                        let mut memory_range =
532                            string_location..(string_location + string_length as u64);
533                        // This memory might need to be read by 32-bit aligned words, so make sure
534                        // the range is aligned to 32 bits.
535                        memory_range.align_to_32_bits();
536                        if !memory_ranges.contains(&memory_range) {
537                            memory_ranges.push(memory_range);
538                        }
539                    }
540                }
541            }
542        }
543        memory_ranges
544    }
545}
546
547#[cfg(test)]
548mod test {
549    use termtree::Tree;
550
551    use crate::{
552        Variable, VariableCache, VariableLocation, VariableName, VariableNodeType, VariableType,
553        VariantRole,
554    };
555
556    fn show_tree(cache: &VariableCache) {
557        let tree = build_tree(cache, cache.root_variable());
558
559        println!("{tree}");
560    }
561
562    fn build_tree(cache: &VariableCache, variable: &Variable) -> Tree<String> {
563        let mut entry = Tree::new(format!(
564            "{:?}: name={:?}, type={:?}, value={:?}",
565            variable.variable_key,
566            variable.name,
567            variable.type_name,
568            variable.to_string(cache)
569        ));
570
571        let children = cache.get_children(variable.variable_key);
572
573        for child in children {
574            entry.push(build_tree(cache, child));
575        }
576
577        entry
578    }
579
580    #[test]
581    fn static_cache() {
582        let c = VariableCache::new_static_cache();
583
584        let cache_variable = c.root_variable();
585
586        println!("{cache_variable:#?}");
587
588        //assert_eq!(cache_variable.parent_key, None);
589        assert_eq!(cache_variable.name, VariableName::StaticScopeRoot);
590        assert_eq!(cache_variable.type_name, VariableType::Unknown);
591        assert_eq!(
592            cache_variable.variable_node_type,
593            VariableNodeType::UnitsLookup
594        );
595
596        assert_eq!(cache_variable.to_string(&c), "<unknown>");
597
598        assert_eq!(cache_variable.source_location, None);
599        assert_eq!(cache_variable.memory_location, VariableLocation::Unknown);
600        assert_eq!(cache_variable.byte_size, None);
601        assert_eq!(cache_variable.role, VariantRole::NonVariant);
602    }
603
604    #[test]
605    fn find_children() {
606        let mut cache = VariableCache::new_static_cache();
607        let root_key = cache.root_variable().variable_key;
608
609        let var_1 = cache.create_variable(root_key, None).unwrap();
610
611        let var_2 = cache.create_variable(root_key, None).unwrap();
612
613        let children: Vec<_> = cache.get_children(root_key).collect();
614
615        let expected_children = vec![&var_1, &var_2];
616
617        assert_eq!(children, expected_children);
618    }
619
620    #[test]
621    fn find_entry() {
622        let mut cache = VariableCache::new_static_cache();
623        let root_key = cache.root_variable().variable_key;
624
625        let var_1 = cache.create_variable(root_key, None).unwrap();
626
627        let _var_2 = cache.create_variable(root_key, None).unwrap();
628
629        assert_eq!(cache.get_variable_by_key(var_1.variable_key), Some(var_1));
630    }
631
632    /// Build up a tree like this:
633    ///
634    /// [root]
635    /// |
636    /// +-- [var_1]
637    /// +-- [var_2]
638    /// |   |
639    /// |   +-- [var_3]
640    /// |   |   |
641    /// |   |   +-- [var_5]
642    /// |   |
643    /// |   +-- [var_4]
644    /// |
645    /// +-- [var_6]
646    ///     |
647    ///     +-- [var_7]
648    fn build_test_tree() -> (VariableCache, Vec<Variable>) {
649        let mut cache = VariableCache::new_static_cache();
650        let root_key = cache.root_variable().variable_key;
651
652        let var_1 = cache.create_variable(root_key, None).unwrap();
653
654        let var_2 = cache.create_variable(root_key, None).unwrap();
655
656        let var_3 = cache.create_variable(var_2.variable_key, None).unwrap();
657
658        let var_4 = cache.create_variable(var_2.variable_key, None).unwrap();
659
660        let var_5 = cache.create_variable(var_3.variable_key, None).unwrap();
661
662        let var_6 = cache.create_variable(root_key, None).unwrap();
663
664        let var_7 = cache.create_variable(var_6.variable_key, None).unwrap();
665
666        assert_eq!(cache.len(), 8);
667
668        let variables = vec![
669            cache.root_variable().clone(),
670            var_1,
671            var_2,
672            var_3,
673            var_4,
674            var_5,
675            var_6,
676            var_7,
677        ];
678
679        (cache, variables)
680    }
681
682    #[test]
683    fn remove_entry() {
684        let (mut cache, vars) = build_test_tree();
685
686        // no children
687        cache.remove_cache_entry(vars[1].variable_key).unwrap();
688        assert!(cache.get_variable_by_key(vars[1].variable_key).is_none());
689        assert_eq!(cache.len(), 7);
690
691        // one child
692        cache.remove_cache_entry(vars[6].variable_key).unwrap();
693        assert!(cache.get_variable_by_key(vars[6].variable_key).is_none());
694        assert!(cache.get_variable_by_key(vars[7].variable_key).is_none());
695        assert_eq!(cache.len(), 5);
696
697        // multi-level children
698        cache.remove_cache_entry(vars[2].variable_key).unwrap();
699        assert!(cache.get_variable_by_key(vars[2].variable_key).is_none());
700        assert!(cache.get_variable_by_key(vars[3].variable_key).is_none());
701        assert!(cache.get_variable_by_key(vars[4].variable_key).is_none());
702        assert!(cache.get_variable_by_key(vars[5].variable_key).is_none());
703        assert_eq!(cache.len(), 1);
704    }
705
706    #[test]
707    fn find_entry_by_name() {
708        let (mut cache, mut vars) = build_test_tree();
709        let non_unique_name = VariableName::Named("non_unique_name".to_string());
710        let unique_name = VariableName::Named("unique_name".to_string());
711
712        show_tree(&cache);
713
714        vars[3].name = non_unique_name.clone();
715        cache.update_variable(&vars[3]).unwrap();
716
717        show_tree(&cache);
718
719        vars[4].name = unique_name.clone();
720        cache.update_variable(&vars[4]).unwrap();
721
722        show_tree(&cache);
723
724        vars[6].name = non_unique_name.clone();
725        cache.update_variable(&vars[6]).unwrap();
726
727        show_tree(&cache);
728
729        assert!(vars[3].variable_key < vars[6].variable_key);
730
731        let var_3 = cache.get_variable_by_name(&non_unique_name).unwrap();
732        assert_eq!(&var_3, &vars[3]);
733
734        let var_4 = cache.get_variable_by_name(&unique_name).unwrap();
735        assert_eq!(&var_4, &vars[4]);
736
737        let var_6 = cache
738            .get_variable_by_name_and_parent(&non_unique_name, vars[6].parent_key)
739            .unwrap();
740        assert_eq!(&var_6, &vars[6]);
741    }
742
743    #[test]
744    fn adopt_grand_children() {
745        let (mut cache, mut vars) = build_test_tree();
746
747        cache.adopt_grand_children(&vars[2], &vars[3]).unwrap();
748
749        assert!(cache.get_variable_by_key(vars[3].variable_key).is_none());
750
751        let new_children: Vec<_> = cache.get_children(vars[2].variable_key).collect();
752
753        vars[5].parent_key = vars[2].variable_key;
754
755        assert_eq!(new_children, vec![&vars[4], &vars[5]]);
756    }
757}