Skip to main content

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::Artificial,
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) if string_length.is_valid() => {
498                            string_length.to_string(self).parse().unwrap_or(0_usize)
499                        }
500                        _ => 0_usize,
501                    };
502                    let string_location = match children.iter().find(|child_variable| {
503                        matches!(child_variable.name, VariableName::Named(ref name ) if name == "data_ptr")
504                    }) {
505                        Some(location_value) => {
506                            let mut child_variables =
507                                self.get_children(location_value.variable_key);
508                            if let Some(first_child) = child_variables.next() {
509                                first_child
510                                    .memory_location
511                                    .memory_address()
512                                    .unwrap_or(0_u64)
513                            } else {
514                                0_u64
515                            }
516                        }
517                        None => 0_u64,
518                    };
519                    if string_location == 0 || string_length == 0 {
520                        // We don't have enough information to read the string from memory.
521                        // I've never seen an instance of this, but it is theoretically possible.
522                        tracing::warn!(
523                            "Failed to find string location or length for variable: {:?}",
524                            variable
525                        );
526                    } else {
527                        let mut memory_range =
528                            string_location..(string_location + string_length as u64);
529                        // This memory might need to be read by 32-bit aligned words, so make sure
530                        // the range is aligned to 32 bits.
531                        memory_range.align_to_32_bits();
532                        if !memory_ranges.contains(&memory_range) {
533                            memory_ranges.push(memory_range);
534                        }
535                    }
536                }
537            }
538        }
539        memory_ranges
540    }
541}
542
543#[cfg(test)]
544mod test {
545    use termtree::Tree;
546
547    use crate::{
548        Variable, VariableCache, VariableLocation, VariableName, VariableNodeType, VariableType,
549        VariantRole,
550    };
551
552    fn show_tree(cache: &VariableCache) {
553        let tree = build_tree(cache, cache.root_variable());
554
555        println!("{tree}");
556    }
557
558    fn build_tree(cache: &VariableCache, variable: &Variable) -> Tree<String> {
559        let mut entry = Tree::new(format!(
560            "{:?}: name={:?}, type={:?}, value={:?}",
561            variable.variable_key,
562            variable.name,
563            variable.type_name,
564            variable.to_string(cache)
565        ));
566
567        let children = cache.get_children(variable.variable_key);
568
569        for child in children {
570            entry.push(build_tree(cache, child));
571        }
572
573        entry
574    }
575
576    #[test]
577    fn static_cache() {
578        let c = VariableCache::new_static_cache();
579
580        let cache_variable = c.root_variable();
581
582        println!("{cache_variable:#?}");
583
584        //assert_eq!(cache_variable.parent_key, None);
585        assert_eq!(cache_variable.name, VariableName::StaticScopeRoot);
586        assert_eq!(cache_variable.type_name, VariableType::Unknown);
587        assert_eq!(
588            cache_variable.variable_node_type,
589            VariableNodeType::UnitsLookup
590        );
591
592        assert_eq!(cache_variable.to_string(&c), "<unknown>");
593
594        assert_eq!(cache_variable.source_location, None);
595        assert_eq!(cache_variable.memory_location, VariableLocation::Unknown);
596        assert_eq!(cache_variable.byte_size, None);
597        assert_eq!(cache_variable.role, VariantRole::NonVariant);
598    }
599
600    #[test]
601    fn find_children() {
602        let mut cache = VariableCache::new_static_cache();
603        let root_key = cache.root_variable().variable_key;
604
605        let var_1 = cache.create_variable(root_key, None).unwrap();
606
607        let var_2 = cache.create_variable(root_key, None).unwrap();
608
609        let children: Vec<_> = cache.get_children(root_key).collect();
610
611        let expected_children = vec![&var_1, &var_2];
612
613        assert_eq!(children, expected_children);
614    }
615
616    #[test]
617    fn find_entry() {
618        let mut cache = VariableCache::new_static_cache();
619        let root_key = cache.root_variable().variable_key;
620
621        let var_1 = cache.create_variable(root_key, None).unwrap();
622
623        let _var_2 = cache.create_variable(root_key, None).unwrap();
624
625        assert_eq!(cache.get_variable_by_key(var_1.variable_key), Some(var_1));
626    }
627
628    /// Build up a tree like this:
629    ///
630    /// [root]
631    /// |
632    /// +-- [var_1]
633    /// +-- [var_2]
634    /// |   |
635    /// |   +-- [var_3]
636    /// |   |   |
637    /// |   |   +-- [var_5]
638    /// |   |
639    /// |   +-- [var_4]
640    /// |
641    /// +-- [var_6]
642    ///     |
643    ///     +-- [var_7]
644    fn build_test_tree() -> (VariableCache, Vec<Variable>) {
645        let mut cache = VariableCache::new_static_cache();
646        let root_key = cache.root_variable().variable_key;
647
648        let var_1 = cache.create_variable(root_key, None).unwrap();
649
650        let var_2 = cache.create_variable(root_key, None).unwrap();
651
652        let var_3 = cache.create_variable(var_2.variable_key, None).unwrap();
653
654        let var_4 = cache.create_variable(var_2.variable_key, None).unwrap();
655
656        let var_5 = cache.create_variable(var_3.variable_key, None).unwrap();
657
658        let var_6 = cache.create_variable(root_key, None).unwrap();
659
660        let var_7 = cache.create_variable(var_6.variable_key, None).unwrap();
661
662        assert_eq!(cache.len(), 8);
663
664        let variables = vec![
665            cache.root_variable().clone(),
666            var_1,
667            var_2,
668            var_3,
669            var_4,
670            var_5,
671            var_6,
672            var_7,
673        ];
674
675        (cache, variables)
676    }
677
678    #[test]
679    fn remove_entry() {
680        let (mut cache, vars) = build_test_tree();
681
682        // no children
683        cache.remove_cache_entry(vars[1].variable_key).unwrap();
684        assert!(cache.get_variable_by_key(vars[1].variable_key).is_none());
685        assert_eq!(cache.len(), 7);
686
687        // one child
688        cache.remove_cache_entry(vars[6].variable_key).unwrap();
689        assert!(cache.get_variable_by_key(vars[6].variable_key).is_none());
690        assert!(cache.get_variable_by_key(vars[7].variable_key).is_none());
691        assert_eq!(cache.len(), 5);
692
693        // multi-level children
694        cache.remove_cache_entry(vars[2].variable_key).unwrap();
695        assert!(cache.get_variable_by_key(vars[2].variable_key).is_none());
696        assert!(cache.get_variable_by_key(vars[3].variable_key).is_none());
697        assert!(cache.get_variable_by_key(vars[4].variable_key).is_none());
698        assert!(cache.get_variable_by_key(vars[5].variable_key).is_none());
699        assert_eq!(cache.len(), 1);
700    }
701
702    #[test]
703    fn find_entry_by_name() {
704        let (mut cache, mut vars) = build_test_tree();
705        let non_unique_name = VariableName::Named("non_unique_name".to_string());
706        let unique_name = VariableName::Named("unique_name".to_string());
707
708        show_tree(&cache);
709
710        vars[3].name = non_unique_name.clone();
711        cache.update_variable(&vars[3]).unwrap();
712
713        show_tree(&cache);
714
715        vars[4].name = unique_name.clone();
716        cache.update_variable(&vars[4]).unwrap();
717
718        show_tree(&cache);
719
720        vars[6].name = non_unique_name.clone();
721        cache.update_variable(&vars[6]).unwrap();
722
723        show_tree(&cache);
724
725        assert!(vars[3].variable_key < vars[6].variable_key);
726
727        let var_3 = cache.get_variable_by_name(&non_unique_name).unwrap();
728        assert_eq!(&var_3, &vars[3]);
729
730        let var_4 = cache.get_variable_by_name(&unique_name).unwrap();
731        assert_eq!(&var_4, &vars[4]);
732
733        let var_6 = cache
734            .get_variable_by_name_and_parent(&non_unique_name, vars[6].parent_key)
735            .unwrap();
736        assert_eq!(&var_6, &vars[6]);
737    }
738
739    #[test]
740    fn adopt_grand_children() {
741        let (mut cache, mut vars) = build_test_tree();
742
743        cache.adopt_grand_children(&vars[2], &vars[3]).unwrap();
744
745        assert!(cache.get_variable_by_key(vars[3].variable_key).is_none());
746
747        let new_children: Vec<_> = cache.get_children(vars[2].variable_key).collect();
748
749        vars[5].parent_key = vars[2].variable_key;
750
751        assert_eq!(new_children, vec![&vars[4], &vars[5]]);
752    }
753}