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                    if out.len() >= max_count {
67                        // Be a bit lenient with the limit, avoid showing "1 more" for a single child.
68                        let remaining = children.clone().count();
69                        if remaining > 1 {
70                            break;
71                        }
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 {} more", remaining),
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`: {:?}. Please report this as a bug",
181                parent_key
182            )));
183        }
184
185        let mut variable_to_add = Variable::new(unit_info);
186        variable_to_add.parent_key = parent_key;
187
188        // The caller is telling us this is definitely a new `Variable`
189        variable_to_add.variable_key = get_object_reference();
190
191        tracing::trace!(
192            "VariableCache: Add Variable: key={:?}, parent={:?}, name={:?}",
193            variable_to_add.variable_key,
194            variable_to_add.parent_key,
195            &variable_to_add.name
196        );
197
198        match self.variable_hash_map.entry(variable_to_add.variable_key) {
199            Entry::Occupied(_) => {
200                return Err(DebugError::Other(format!(
201                    "Attempt to insert a new `Variable`:{:?} with a duplicate cache key: {:?}. Please report this as a bug.",
202                    variable_to_add.name, variable_to_add.variable_key
203                )));
204            }
205            Entry::Vacant(entry) => {
206                entry.insert(variable_to_add.clone());
207            }
208        }
209
210        Ok(variable_to_add)
211    }
212
213    /// Add a variable to the cache
214    ///
215    /// The parent key must exist in the cache, and the variable
216    /// must not have a key assigned yet.
217    pub fn add_variable(
218        &mut self,
219        parent_key: ObjectRef,
220        cache_variable: &mut Variable,
221    ) -> Result<(), DebugError> {
222        // Validate that the parent_key exists ...
223        if !self.variable_hash_map.contains_key(&parent_key) {
224            return Err(DebugError::Other(format!(
225                "VariableCache: Attempted to add a new variable: {} with non existent `parent_key`: {:?}. Please report this as a bug",
226                cache_variable.name, parent_key
227            )));
228        }
229
230        cache_variable.parent_key = parent_key;
231
232        if cache_variable.variable_key != ObjectRef::Invalid {
233            return Err(DebugError::Other(format!(
234                "VariableCache: Attempted to add a new variable: {} with already set key: {:?}. Please report this as a bug",
235                cache_variable.name, cache_variable.variable_key
236            )));
237        }
238
239        // The caller is telling us this is definitely a new `Variable`
240        cache_variable.variable_key = get_object_reference();
241
242        tracing::trace!(
243            "VariableCache: Add Variable: key={:?}, parent={:?}, name={:?}",
244            cache_variable.variable_key,
245            cache_variable.parent_key,
246            cache_variable.name
247        );
248
249        if let Some(old_variable) = self
250            .variable_hash_map
251            .insert(cache_variable.variable_key, cache_variable.clone())
252        {
253            return Err(DebugError::Other(format!(
254                "Attempt to insert a new `Variable`:{:?} with a duplicate cache key: {:?}. Please report this as a bug.",
255                cache_variable.name, old_variable.variable_key
256            )));
257        }
258
259        Ok(())
260    }
261
262    /// Update a variable in the cache
263    ///
264    /// This function does not update the value of the variable.
265    pub fn update_variable(&mut self, cache_variable: &Variable) -> Result<(), DebugError> {
266        // Attempt to update an existing `Variable` in the cache
267        tracing::trace!(
268            "VariableCache: Update Variable, key={:?}, name={:?}",
269            cache_variable.variable_key,
270            &cache_variable.name
271        );
272
273        let Some(prev_entry) = self.variable_hash_map.get_mut(&cache_variable.variable_key) else {
274            return Err(DebugError::Other(format!(
275                "Attempt to update an existing `Variable`:{:?} with a non-existent cache key: {:?}. Please report this as a bug.",
276                cache_variable.name, cache_variable.variable_key
277            )));
278        };
279
280        if cache_variable != prev_entry {
281            tracing::trace!("Updated:  {:?}", cache_variable);
282            tracing::trace!("Previous: {:?}", prev_entry);
283            *prev_entry = cache_variable.clone();
284        }
285
286        Ok(())
287    }
288
289    /// Retrieve a clone of a specific `Variable`, using the `variable_key`.
290    pub fn get_variable_by_key(&self, variable_key: ObjectRef) -> Option<Variable> {
291        self.variable_hash_map.get(&variable_key).cloned()
292    }
293
294    /// Retrieve a clone of a specific `Variable`, using the `name` and `parent_key`.
295    /// If there is more than one, it will be logged (tracing::error!), and only the last will be returned.
296    pub fn get_variable_by_name_and_parent(
297        &self,
298        variable_name: &VariableName,
299        parent_key: ObjectRef,
300    ) -> Option<Variable> {
301        let child_variables = self.variable_hash_map.values().filter(|child_variable| {
302            &child_variable.name == variable_name && child_variable.parent_key == parent_key
303        });
304
305        // Clone the iterator. This is cheap and makes rewinding easier.
306        let mut first_iter = child_variables.clone();
307        let first = first_iter.next();
308        let more = first_iter.next().is_some();
309
310        if more {
311            let (last_index, last) = child_variables.enumerate().last().unwrap();
312            tracing::error!(
313                "Found {} variables with parent_key={:?} and name={}. Please report this as a bug.",
314                last_index + 1,
315                parent_key,
316                variable_name
317            );
318            Some(last.clone())
319        } else {
320            first.cloned()
321        }
322    }
323
324    /// Retrieve a clone of a specific `Variable`, using the `name`.
325    /// If there is more than one, it will be logged (tracing::warn!), and only the first will be returned.
326    /// It is possible for a hierarchy of variables in a cache to have duplicate names under different parents.
327    pub fn get_variable_by_name(&self, variable_name: &VariableName) -> Option<Variable> {
328        let mut child_variables = self
329            .variable_hash_map
330            .values()
331            .filter(|child_variable| child_variable.name.eq(variable_name));
332
333        let first = child_variables.next();
334        let more = child_variables.next().is_some();
335
336        if more {
337            tracing::warn!(
338                "Found {} variables with name={}. Please report this as a bug.",
339                self.variable_hash_map.len(),
340                variable_name
341            );
342        }
343
344        first.cloned()
345    }
346
347    /// Retrieve `clone`d version of all the children of a `Variable`.
348    /// If `parent_key == None`, it will return all the top level variables (no parents) in this cache.
349    pub fn get_children(&self, parent_key: ObjectRef) -> impl Iterator<Item = &Variable> + Clone {
350        self.variable_hash_map
351            .values()
352            .filter(move |child_variable| child_variable.parent_key == parent_key)
353    }
354
355    /// Check if variable has children. If the variable doesn't exist, it will return false.
356    pub fn has_children(&self, parent_variable: &Variable) -> bool {
357        self.get_children(parent_variable.variable_key)
358            .next()
359            .is_some()
360    }
361
362    /// Sometimes DWARF uses intermediate nodes that are not part of the coded variable structure.
363    /// 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`
364    ///
365    /// Similarly, while resolving [VariableNodeType::is_deferred()], i.e. 'lazy load' of variables, we need to create intermediate variables that are eliminated here.
366    ///
367    /// NOTE: For all other situations, this function will silently do nothing.
368    pub fn adopt_grand_children(
369        &mut self,
370        parent_variable: &Variable,
371        obsolete_child_variable: &Variable,
372    ) -> Result<(), Error> {
373        if obsolete_child_variable.type_name == VariableType::Unknown
374            || obsolete_child_variable.variable_node_type != VariableNodeType::DoNotRecurse
375        {
376            // Make sure we pass children up, past any intermediate nodes.
377            self.variable_hash_map
378                .values_mut()
379                .filter(|search_variable| {
380                    search_variable.parent_key == obsolete_child_variable.variable_key
381                })
382                .for_each(|grand_child| grand_child.parent_key = parent_variable.variable_key);
383            // Remove the intermediate variable from the cache
384            self.remove_cache_entry(obsolete_child_variable.variable_key)?;
385        }
386        Ok(())
387    }
388
389    /// Removing an entry's children from the `VariableCache` will recursively remove all their children
390    pub fn remove_cache_entry_children(
391        &mut self,
392        parent_variable_key: ObjectRef,
393    ) -> Result<(), Error> {
394        let children = self
395            .variable_hash_map
396            .values()
397            .filter(|child_variable| child_variable.parent_key == parent_variable_key)
398            .cloned()
399            .collect::<Vec<Variable>>();
400
401        for child in children {
402            self.remove_cache_entry(child.variable_key)?;
403        }
404
405        Ok(())
406    }
407    /// Removing an entry from the `VariableCache` will recursively remove all its children
408    pub fn remove_cache_entry(&mut self, variable_key: ObjectRef) -> Result<(), Error> {
409        self.remove_cache_entry_children(variable_key)?;
410        if self.variable_hash_map.remove(&variable_key).is_none() {
411            return Err(Error::Other(format!(
412                "Failed to remove a `VariableCache` entry with key: {:?}. Please report this as a bug.",
413                variable_key
414            )));
415        };
416        Ok(())
417    }
418    /// Recursively process the deferred variables in the variable cache,
419    /// and add their children to the cache.
420    /// Enforce a max level, so that we don't recurse infinitely on circular references.
421    pub fn recurse_deferred_variables(
422        &mut self,
423        debug_info: &DebugInfo,
424        memory: &mut dyn MemoryInterface,
425        max_recursion_depth: usize,
426        frame_info: StackFrameInfo<'_>,
427    ) {
428        let mut parent_variable = self.root_variable().clone();
429
430        self.recurse_deferred_variables_internal(
431            debug_info,
432            memory,
433            &mut parent_variable,
434            max_recursion_depth,
435            0,
436            frame_info,
437        )
438    }
439
440    fn recurse_deferred_variables_internal(
441        &mut self,
442        debug_info: &DebugInfo,
443        memory: &mut dyn MemoryInterface,
444        parent_variable: &mut Variable,
445        max_recursion_depth: usize,
446        current_recursion_depth: usize,
447        frame_info: StackFrameInfo<'_>,
448    ) {
449        if current_recursion_depth >= max_recursion_depth {
450            return;
451        }
452
453        if debug_info
454            .cache_deferred_variables(self, memory, parent_variable, frame_info)
455            .is_err()
456        {
457            return;
458        };
459
460        let children: Vec<_> = self
461            .get_children(parent_variable.variable_key)
462            .cloned()
463            .collect();
464
465        for mut child in children {
466            self.recurse_deferred_variables_internal(
467                debug_info,
468                memory,
469                &mut child,
470                max_recursion_depth,
471                current_recursion_depth + 1,
472                frame_info,
473            );
474        }
475    }
476
477    /// Traverse the `VariableCache` and return a Vec of all the memory ranges that are referenced by the variables.
478    /// This is used to determine which memory ranges to read from the target when creating a 'default' [`crate::CoreDump`].
479    pub fn get_discrete_memory_ranges(&self) -> Vec<Range<u64>> {
480        let mut memory_ranges: Vec<Range<u64>> = Vec::new();
481        for variable in self.variable_hash_map.values() {
482            if let Some(mut memory_range) = variable.memory_range() {
483                // This memory may need to be read by 32-bit aligned words, so make sure
484                // the range is aligned to 32 bits.
485                memory_range.align_to_32_bits();
486                if !memory_ranges.contains(&memory_range) {
487                    memory_ranges.push(memory_range);
488                }
489            }
490
491            // The datatype &str is a special case, because it is stores a pointer to the string data,
492            // and the length of the string.
493            if matches!(variable.type_name, VariableType::Struct(ref name) if name == "&str") {
494                let children: Vec<_> = self.get_children(variable.variable_key).collect();
495                if !children.is_empty() {
496                    let string_length = match children.iter().find(|child_variable| {
497                        matches!(child_variable.name, VariableName::Named(ref name) if name == "length")
498                    }) {
499                        Some(string_length) => {
500                            if string_length.is_valid() {
501                                string_length.to_string(self).parse().unwrap_or(0_usize)
502                            } else {
503                                0_usize
504                            }
505                        }
506                        None => 0_usize,
507                    };
508                    let string_location = match children.iter().find(|child_variable| {
509                        matches!(child_variable.name, VariableName::Named(ref name ) if name == "data_ptr")
510                    }) {
511                        Some(location_value) => {
512                            let mut child_variables =
513                                self.get_children(location_value.variable_key);
514                            if let Some(first_child) = child_variables.next() {
515                                first_child
516                                    .memory_location
517                                    .memory_address()
518                                    .unwrap_or(0_u64)
519                            } else {
520                                0_u64
521                            }
522                        }
523                        None => 0_u64,
524                    };
525                    if string_location == 0 || string_length == 0 {
526                        // We don't have enough information to read the string from memory.
527                        // I've never seen an instance of this, but it is theoretically possible.
528                        tracing::warn!(
529                            "Failed to find string location or length for variable: {:?}",
530                            variable
531                        );
532                    } else {
533                        let mut memory_range =
534                            string_location..(string_location + string_length as u64);
535                        // This memory might need to be read by 32-bit aligned words, so make sure
536                        // the range is aligned to 32 bits.
537                        memory_range.align_to_32_bits();
538                        if !memory_ranges.contains(&memory_range) {
539                            memory_ranges.push(memory_range);
540                        }
541                    }
542                }
543            }
544        }
545        memory_ranges
546    }
547}
548
549#[cfg(test)]
550mod test {
551    use termtree::Tree;
552
553    use crate::{
554        Variable, VariableCache, VariableLocation, VariableName, VariableNodeType, VariableType,
555        VariantRole,
556    };
557
558    fn show_tree(cache: &VariableCache) {
559        let tree = build_tree(cache, cache.root_variable());
560
561        println!("{}", tree);
562    }
563
564    fn build_tree(cache: &VariableCache, variable: &Variable) -> Tree<String> {
565        let mut entry = Tree::new(format!(
566            "{:?}: name={:?}, type={:?}, value={:?}",
567            variable.variable_key,
568            variable.name,
569            variable.type_name,
570            variable.to_string(cache)
571        ));
572
573        let children = cache.get_children(variable.variable_key);
574
575        for child in children {
576            entry.push(build_tree(cache, child));
577        }
578
579        entry
580    }
581
582    #[test]
583    fn static_cache() {
584        let c = VariableCache::new_static_cache();
585
586        let cache_variable = c.root_variable();
587
588        println!("{:#?}", cache_variable);
589
590        //assert_eq!(cache_variable.parent_key, None);
591        assert_eq!(cache_variable.name, VariableName::StaticScopeRoot);
592        assert_eq!(cache_variable.type_name, VariableType::Unknown);
593        assert_eq!(
594            cache_variable.variable_node_type,
595            VariableNodeType::UnitsLookup
596        );
597
598        assert_eq!(cache_variable.to_string(&c), "<unknown>");
599
600        assert_eq!(cache_variable.source_location, None);
601        assert_eq!(cache_variable.memory_location, VariableLocation::Unknown);
602        assert_eq!(cache_variable.byte_size, None);
603        assert_eq!(cache_variable.role, VariantRole::NonVariant);
604    }
605
606    #[test]
607    fn find_children() {
608        let mut cache = VariableCache::new_static_cache();
609        let root_key = cache.root_variable().variable_key;
610
611        let var_1 = cache.create_variable(root_key, None).unwrap();
612
613        let var_2 = cache.create_variable(root_key, None).unwrap();
614
615        let children: Vec<_> = cache.get_children(root_key).collect();
616
617        let expected_children = vec![&var_1, &var_2];
618
619        assert_eq!(children, expected_children);
620    }
621
622    #[test]
623    fn find_entry() {
624        let mut cache = VariableCache::new_static_cache();
625        let root_key = cache.root_variable().variable_key;
626
627        let var_1 = cache.create_variable(root_key, None).unwrap();
628
629        let _var_2 = cache.create_variable(root_key, None).unwrap();
630
631        assert_eq!(cache.get_variable_by_key(var_1.variable_key), Some(var_1));
632    }
633
634    /// Build up a tree like this:
635    ///
636    /// [root]
637    /// |
638    /// +-- [var_1]
639    /// +-- [var_2]
640    /// |   |
641    /// |   +-- [var_3]
642    /// |   |   |
643    /// |   |   +-- [var_5]
644    /// |   |
645    /// |   +-- [var_4]
646    /// |
647    /// +-- [var_6]
648    ///     |
649    ///     +-- [var_7]
650    fn build_test_tree() -> (VariableCache, Vec<Variable>) {
651        let mut cache = VariableCache::new_static_cache();
652        let root_key = cache.root_variable().variable_key;
653
654        let var_1 = cache.create_variable(root_key, None).unwrap();
655
656        let var_2 = cache.create_variable(root_key, None).unwrap();
657
658        let var_3 = cache.create_variable(var_2.variable_key, None).unwrap();
659
660        let var_4 = cache.create_variable(var_2.variable_key, None).unwrap();
661
662        let var_5 = cache.create_variable(var_3.variable_key, None).unwrap();
663
664        let var_6 = cache.create_variable(root_key, None).unwrap();
665
666        let var_7 = cache.create_variable(var_6.variable_key, None).unwrap();
667
668        assert_eq!(cache.len(), 8);
669
670        let variables = vec![
671            cache.root_variable().clone(),
672            var_1,
673            var_2,
674            var_3,
675            var_4,
676            var_5,
677            var_6,
678            var_7,
679        ];
680
681        (cache, variables)
682    }
683
684    #[test]
685    fn remove_entry() {
686        let (mut cache, vars) = build_test_tree();
687
688        // no children
689        cache.remove_cache_entry(vars[1].variable_key).unwrap();
690        assert!(cache.get_variable_by_key(vars[1].variable_key).is_none());
691        assert_eq!(cache.len(), 7);
692
693        // one child
694        cache.remove_cache_entry(vars[6].variable_key).unwrap();
695        assert!(cache.get_variable_by_key(vars[6].variable_key).is_none());
696        assert!(cache.get_variable_by_key(vars[7].variable_key).is_none());
697        assert_eq!(cache.len(), 5);
698
699        // multi-level children
700        cache.remove_cache_entry(vars[2].variable_key).unwrap();
701        assert!(cache.get_variable_by_key(vars[2].variable_key).is_none());
702        assert!(cache.get_variable_by_key(vars[3].variable_key).is_none());
703        assert!(cache.get_variable_by_key(vars[4].variable_key).is_none());
704        assert!(cache.get_variable_by_key(vars[5].variable_key).is_none());
705        assert_eq!(cache.len(), 1);
706    }
707
708    #[test]
709    fn find_entry_by_name() {
710        let (mut cache, mut vars) = build_test_tree();
711        let non_unique_name = VariableName::Named("non_unique_name".to_string());
712        let unique_name = VariableName::Named("unique_name".to_string());
713
714        show_tree(&cache);
715
716        vars[3].name = non_unique_name.clone();
717        cache.update_variable(&vars[3]).unwrap();
718
719        show_tree(&cache);
720
721        vars[4].name = unique_name.clone();
722        cache.update_variable(&vars[4]).unwrap();
723
724        show_tree(&cache);
725
726        vars[6].name = non_unique_name.clone();
727        cache.update_variable(&vars[6]).unwrap();
728
729        show_tree(&cache);
730
731        assert!(vars[3].variable_key < vars[6].variable_key);
732
733        let var_3 = cache.get_variable_by_name(&non_unique_name).unwrap();
734        assert_eq!(&var_3, &vars[3]);
735
736        let var_4 = cache.get_variable_by_name(&unique_name).unwrap();
737        assert_eq!(&var_4, &vars[4]);
738
739        let var_6 = cache
740            .get_variable_by_name_and_parent(&non_unique_name, vars[6].parent_key)
741            .unwrap();
742        assert_eq!(&var_6, &vars[6]);
743    }
744
745    #[test]
746    fn adopt_grand_children() {
747        let (mut cache, mut vars) = build_test_tree();
748
749        cache.adopt_grand_children(&vars[2], &vars[3]).unwrap();
750
751        assert!(cache.get_variable_by_key(vars[3].variable_key).is_none());
752
753        let new_children: Vec<_> = cache.get_children(vars[2].variable_key).collect();
754
755        vars[5].parent_key = vars[2].variable_key;
756
757        assert_eq!(new_children, vec![&vars[4], &vars[5]]);
758    }
759}