1use crate::core::types::{AllocationInfo, TrackingResult};
7use crate::{analysis::CircularReferenceNode, variable_registry::VariableInfo};
8use serde::{Deserialize, Serialize};
9use std::collections::{HashMap, HashSet};
10
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
13pub enum RelationshipType {
14 References,
16 Owns,
18 Clones,
20 Contains,
22 DependsOn,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct VariableNode {
29 pub id: String,
31 pub name: String,
33 pub type_name: String,
35 pub size: usize,
37 pub scope: String,
39 pub is_active: bool,
41 pub category: VariableCategory,
43 pub smart_pointer_info: Option<SmartPointerInfo>,
45 pub created_at: u64,
47 pub destroyed_at: Option<u64>,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
53pub enum VariableCategory {
54 UserVariable,
56 SystemAllocation,
58 SmartPointer,
60 Collection,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct SmartPointerInfo {
67 pub pointer_type: String,
69 pub ref_count: Option<usize>,
71 pub data_ptr: Option<usize>,
73 pub clones: Vec<usize>,
75 pub cloned_from: Option<usize>,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct VariableRelationship {
82 pub source: String,
84 pub target: String,
86 pub relationship_type: RelationshipType,
88 pub weight: f64,
90 pub metadata: HashMap<String, serde_json::Value>,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct VariableCluster {
97 pub id: String,
99 pub cluster_type: ClusterType,
101 pub variables: Vec<String>,
103 pub layout_hint: Option<LayoutHint>,
105 pub metadata: HashMap<String, serde_json::Value>,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
111pub enum ClusterType {
112 Scope,
114 Type,
116 Lifetime,
118 SmartPointerGroup,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct LayoutHint {
125 pub x: f64,
127 pub y: f64,
129 pub width: Option<f64>,
131 pub height: Option<f64>,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct VariableRelationshipGraph {
138 pub nodes: Vec<VariableNode>,
140 pub relationships: Vec<VariableRelationship>,
142 pub clusters: Vec<VariableCluster>,
144 pub statistics: GraphStatistics,
146 pub metadata: HashMap<String, serde_json::Value>,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct GraphStatistics {
153 pub total_nodes: usize,
155 pub total_relationships: usize,
157 pub circular_references: usize,
159 pub largest_cluster_size: usize,
161 pub isolated_nodes: usize,
163 pub avg_relationships_per_node: f64,
165}
166
167pub struct VariableRelationshipBuilder {
169 nodes: HashMap<String, VariableNode>,
170 relationships: Vec<VariableRelationship>,
171 clusters: Vec<VariableCluster>,
172}
173
174impl VariableRelationshipBuilder {
175 pub fn new() -> Self {
177 Self {
178 nodes: HashMap::new(),
179 relationships: Vec::new(),
180 clusters: Vec::new(),
181 }
182 }
183
184 pub fn add_allocations(
186 mut self,
187 allocations: &[AllocationInfo],
188 registry: &HashMap<usize, VariableInfo>,
189 ) -> Self {
190 for alloc in allocations {
191 let node = self.create_node_from_allocation(alloc, registry);
192 self.nodes.insert(node.id.clone(), node);
193 }
194 self
195 }
196
197 pub fn detect_references(mut self) -> Self {
199 let nodes: Vec<_> = self.nodes.values().cloned().collect();
201
202 for node in &nodes {
203 if let Some(smart_ptr_info) = &node.smart_pointer_info {
204 for &clone_addr in &smart_ptr_info.clones {
206 let clone_id = format!("0x{clone_addr:x}");
207 if self.nodes.contains_key(&clone_id) {
208 self.relationships.push(VariableRelationship {
209 source: node.id.clone(),
210 target: clone_id,
211 relationship_type: RelationshipType::Clones,
212 weight: 1.0,
213 metadata: HashMap::new(),
214 });
215 }
216 }
217
218 if let Some(cloned_from_addr) = smart_ptr_info.cloned_from {
220 let parent_id = format!("0x{cloned_from_addr:x}");
221 if self.nodes.contains_key(&parent_id) {
222 self.relationships.push(VariableRelationship {
223 source: parent_id,
224 target: node.id.clone(),
225 relationship_type: RelationshipType::Clones,
226 weight: 1.0,
227 metadata: HashMap::new(),
228 });
229 }
230 }
231
232 if smart_ptr_info.pointer_type == "Box" {
234 if let Some(data_ptr) = smart_ptr_info.data_ptr {
235 let data_id = format!("0x{data_ptr:x}");
236 if self.nodes.contains_key(&data_id) {
237 self.relationships.push(VariableRelationship {
238 source: node.id.clone(),
239 target: data_id,
240 relationship_type: RelationshipType::Owns,
241 weight: 1.0,
242 metadata: HashMap::new(),
243 });
244 }
245 }
246 }
247 }
248 }
249
250 self
251 }
252
253 pub fn detect_scope_relationships(mut self) -> Self {
255 let mut scope_groups: HashMap<String, Vec<String>> = HashMap::new();
257
258 for node in self.nodes.values() {
259 scope_groups
260 .entry(node.scope.clone())
261 .or_default()
262 .push(node.id.clone());
263 }
264
265 for (scope_name, variable_ids) in scope_groups {
267 if variable_ids.len() > 1 {
268 self.clusters.push(VariableCluster {
270 id: format!("scope_{scope_name}"),
271 cluster_type: ClusterType::Scope,
272 variables: variable_ids.clone(),
273 layout_hint: None,
274 metadata: {
275 let mut meta = HashMap::new();
276 meta.insert(
277 "scope_name".to_string(),
278 serde_json::Value::String(scope_name.clone()),
279 );
280 meta
281 },
282 });
283
284 for i in 0..variable_ids.len() {
286 for j in (i + 1)..variable_ids.len() {
287 self.relationships.push(VariableRelationship {
288 source: variable_ids[i].clone(),
289 target: variable_ids[j].clone(),
290 relationship_type: RelationshipType::Contains,
291 weight: 0.3, metadata: {
293 let mut meta = HashMap::new();
294 meta.insert(
295 "scope".to_string(),
296 serde_json::Value::String(scope_name.clone()),
297 );
298 meta
299 },
300 });
301 }
302 }
303 }
304 }
305
306 self
307 }
308
309 pub fn detect_circular_references(mut self) -> Self {
311 let allocations: Vec<AllocationInfo> = self
313 .nodes
314 .values()
315 .filter_map(|node| self.node_to_allocation_info(node))
316 .collect();
317
318 let circular_analysis =
319 crate::analysis::circular_reference::detect_circular_references(&allocations);
320
321 for cycle in &circular_analysis.circular_references {
323 for window in cycle.cycle_path.windows(2) {
324 if let (Some(source), Some(target)) = (window.first(), window.last()) {
325 let source_id = format!("0x{:p}", source as *const CircularReferenceNode);
326 let target_id = format!("0x{:p}", target as *const CircularReferenceNode);
327
328 if self.nodes.contains_key(&source_id) && self.nodes.contains_key(&target_id) {
329 self.relationships.push(VariableRelationship {
330 source: source_id,
331 target: target_id,
332 relationship_type: RelationshipType::DependsOn,
333 weight: 0.8, metadata: {
335 let mut meta = HashMap::new();
336 meta.insert(
337 "circular_reference".to_string(),
338 serde_json::Value::Bool(true),
339 );
340 meta.insert(
341 "cycle_id".to_string(),
342 serde_json::Value::String(format!(
343 "cycle_{}",
344 cycle.cycle_path.len()
345 )),
346 );
347 meta
348 },
349 });
350 }
351 }
352 }
353 }
354
355 self
356 }
357
358 pub fn build_graph(self) -> VariableRelationshipGraph {
360 let statistics = self.calculate_statistics();
361
362 VariableRelationshipGraph {
363 nodes: self.nodes.into_values().collect(),
364 relationships: self.relationships,
365 clusters: self.clusters,
366 statistics,
367 metadata: {
368 let mut meta = HashMap::new();
369 meta.insert(
370 "build_timestamp".to_string(),
371 serde_json::Value::Number(serde_json::Number::from(
372 std::time::SystemTime::now()
373 .duration_since(std::time::UNIX_EPOCH)
374 .unwrap_or_default()
375 .as_secs(),
376 )),
377 );
378 meta
379 },
380 }
381 }
382
383 fn create_node_from_allocation(
385 &self,
386 alloc: &AllocationInfo,
387 registry: &HashMap<usize, VariableInfo>,
388 ) -> VariableNode {
389 let id = format!("0x{:x}", alloc.ptr);
390
391 let (name, type_name, category) = if let Some(var_info) = registry.get(&alloc.ptr) {
393 (
394 var_info.var_name.clone(),
395 var_info.type_name.clone(),
396 VariableCategory::UserVariable,
397 )
398 } else if let (Some(var_name), Some(type_name)) = (&alloc.var_name, &alloc.type_name) {
399 (
400 var_name.clone(),
401 type_name.clone(),
402 VariableCategory::UserVariable,
403 )
404 } else {
405 let (inferred_name, inferred_type) =
406 crate::variable_registry::VariableRegistry::infer_allocation_info_cached(alloc);
407 let category = if inferred_type.contains("Vec") || inferred_type.contains("HashMap") {
408 VariableCategory::Collection
409 } else if inferred_type.contains("Box")
410 || inferred_type.contains("Rc")
411 || inferred_type.contains("Arc")
412 {
413 VariableCategory::SmartPointer
414 } else {
415 VariableCategory::SystemAllocation
416 };
417 (inferred_name, inferred_type, category)
418 };
419
420 let scope = alloc
421 .scope_name
422 .as_deref()
423 .unwrap_or_else(|| {
424 if category == VariableCategory::UserVariable {
425 "main"
426 } else {
427 "system"
428 }
429 })
430 .to_string();
431
432 VariableNode {
433 id,
434 name,
435 type_name,
436 size: alloc.size,
437 scope,
438 is_active: alloc.timestamp_dealloc.is_none(),
439 category,
440 smart_pointer_info: alloc
441 .smart_pointer_info
442 .as_ref()
443 .map(|info| SmartPointerInfo {
444 pointer_type: format!("{:?}", info.pointer_type),
445 ref_count: Some(
446 info.ref_count_history
447 .last()
448 .map(|s| s.strong_count)
449 .unwrap_or(0),
450 ),
451 data_ptr: Some(info.data_ptr),
452 clones: info.clones.clone(),
453 cloned_from: info.cloned_from,
454 }),
455 created_at: alloc.timestamp_alloc,
456 destroyed_at: alloc.timestamp_dealloc,
457 }
458 }
459
460 fn node_to_allocation_info(&self, node: &VariableNode) -> Option<AllocationInfo> {
462 let ptr = usize::from_str_radix(&node.id[2..], 16).ok()?;
463
464 Some(AllocationInfo::new(ptr, node.size))
466 }
467
468 fn calculate_statistics(&self) -> GraphStatistics {
470 let total_nodes = self.nodes.len();
471 let total_relationships = self.relationships.len();
472
473 let circular_references = self
474 .relationships
475 .iter()
476 .filter(|rel| {
477 rel.metadata
478 .get("circular_reference")
479 .and_then(|v| v.as_bool())
480 .unwrap_or(false)
481 })
482 .count();
483
484 let largest_cluster_size = self
485 .clusters
486 .iter()
487 .map(|cluster| cluster.variables.len())
488 .max()
489 .unwrap_or(0);
490
491 let mut connected_nodes = HashSet::new();
493 for rel in &self.relationships {
494 connected_nodes.insert(&rel.source);
495 connected_nodes.insert(&rel.target);
496 }
497 let isolated_nodes = total_nodes - connected_nodes.len();
498
499 let avg_relationships_per_node = if total_nodes > 0 {
500 total_relationships as f64 / total_nodes as f64
501 } else {
502 0.0
503 };
504
505 GraphStatistics {
506 total_nodes,
507 total_relationships,
508 circular_references,
509 largest_cluster_size,
510 isolated_nodes,
511 avg_relationships_per_node,
512 }
513 }
514}
515
516impl Default for VariableRelationshipBuilder {
517 fn default() -> Self {
518 Self::new()
519 }
520}
521
522pub fn build_variable_relationship_graph(
524 allocations: &[AllocationInfo],
525 registry: &HashMap<usize, VariableInfo>,
526) -> TrackingResult<VariableRelationshipGraph> {
527 let graph = VariableRelationshipBuilder::new()
528 .add_allocations(allocations, registry)
529 .detect_references()
530 .detect_scope_relationships()
531 .detect_circular_references()
532 .build_graph();
533
534 Ok(graph)
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540 use crate::core::types::{
541 AllocationInfo, RefCountSnapshot, SmartPointerInfo as CoreSmartPointerInfo,
542 SmartPointerType,
543 };
544 use crate::variable_registry::VariableInfo;
545
546 fn create_test_allocation(
548 ptr: usize,
549 size: usize,
550 var_name: Option<String>,
551 type_name: Option<String>,
552 scope_name: Option<String>,
553 ) -> AllocationInfo {
554 AllocationInfo {
555 ptr,
556 size,
557 var_name,
558 type_name,
559 scope_name,
560 timestamp_alloc: 1000,
561 timestamp_dealloc: None,
562 thread_id: "test_thread".to_string(),
563 borrow_count: 0,
564 stack_trace: Some(vec!["test_function".to_string()]),
565 is_leaked: false,
566 lifetime_ms: None,
567 borrow_info: None,
568 clone_info: None,
569 ownership_history_available: false,
570 smart_pointer_info: None,
571 memory_layout: None,
572 generic_info: None,
573 dynamic_type_info: None,
574 runtime_state: None,
575 stack_allocation: None,
576 temporary_object: None,
577 fragmentation_analysis: None,
578 generic_instantiation: None,
579 type_relationships: None,
580 type_usage: None,
581 function_call_tracking: None,
582 lifecycle_tracking: None,
583 access_tracking: None,
584 drop_chain_analysis: None,
585 }
586 }
587
588 #[allow(clippy::too_many_arguments)]
590 fn create_smart_pointer_allocation(
591 ptr: usize,
592 size: usize,
593 var_name: String,
594 type_name: String,
595 pointer_type: SmartPointerType,
596 data_ptr: usize,
597 clones: Vec<usize>,
598 cloned_from: Option<usize>,
599 ) -> AllocationInfo {
600 let mut alloc = create_test_allocation(
601 ptr,
602 size,
603 Some(var_name),
604 Some(type_name),
605 Some("main".to_string()),
606 );
607
608 alloc.smart_pointer_info = Some(CoreSmartPointerInfo {
609 data_ptr,
610 pointer_type,
611 ref_count_history: vec![RefCountSnapshot {
612 timestamp: 1000,
613 strong_count: 1,
614 weak_count: 0,
615 }],
616 weak_count: Some(0),
617 is_data_owner: true,
618 is_weak_reference: false,
619 is_implicitly_deallocated: false,
620 clones,
621 cloned_from,
622 });
623
624 alloc
625 }
626
627 fn create_test_variable_info(var_name: String, type_name: String) -> VariableInfo {
629 VariableInfo {
630 var_name,
631 type_name,
632 timestamp: 1000,
633 size: 64,
634 thread_id: 1,
635 memory_usage: 64,
636 }
637 }
638
639 #[test]
640 fn test_relationship_type_serialization() {
641 let relationship_types = vec![
642 RelationshipType::References,
643 RelationshipType::Owns,
644 RelationshipType::Clones,
645 RelationshipType::Contains,
646 RelationshipType::DependsOn,
647 ];
648
649 for rel_type in relationship_types {
650 let serialized = serde_json::to_string(&rel_type).expect("Failed to serialize");
651 let _deserialized: RelationshipType =
652 serde_json::from_str(&serialized).expect("Failed to deserialize");
653 }
654 }
655
656 #[test]
657 fn test_variable_category_serialization() {
658 let categories = vec![
659 VariableCategory::UserVariable,
660 VariableCategory::SystemAllocation,
661 VariableCategory::SmartPointer,
662 VariableCategory::Collection,
663 ];
664
665 for category in categories {
666 let serialized = serde_json::to_string(&category).expect("Failed to serialize");
667 let _deserialized: VariableCategory =
668 serde_json::from_str(&serialized).expect("Failed to deserialize");
669 }
670 }
671
672 #[test]
673 fn test_cluster_type_serialization() {
674 let cluster_types = vec![
675 ClusterType::Scope,
676 ClusterType::Type,
677 ClusterType::Lifetime,
678 ClusterType::SmartPointerGroup,
679 ];
680
681 for cluster_type in cluster_types {
682 let serialized = serde_json::to_string(&cluster_type).expect("Failed to serialize");
683 let _deserialized: ClusterType =
684 serde_json::from_str(&serialized).expect("Failed to deserialize");
685 }
686 }
687
688 #[test]
689 fn test_variable_relationship_builder_creation() {
690 let builder = VariableRelationshipBuilder::new();
691 assert!(builder.nodes.is_empty());
692 assert!(builder.relationships.is_empty());
693 assert!(builder.clusters.is_empty());
694 }
695
696 #[test]
697 fn test_variable_relationship_builder_default() {
698 let builder = VariableRelationshipBuilder::default();
699 assert!(builder.nodes.is_empty());
700 assert!(builder.relationships.is_empty());
701 assert!(builder.clusters.is_empty());
702 }
703
704 #[test]
705 fn test_add_allocations_basic() {
706 let allocations = vec![
707 create_test_allocation(
708 0x1000,
709 1024,
710 Some("var1".to_string()),
711 Some("i32".to_string()),
712 Some("main".to_string()),
713 ),
714 create_test_allocation(
715 0x2000,
716 512,
717 Some("var2".to_string()),
718 Some("String".to_string()),
719 Some("main".to_string()),
720 ),
721 ];
722
723 let registry = HashMap::new();
724
725 let builder = VariableRelationshipBuilder::new().add_allocations(&allocations, ®istry);
726
727 assert_eq!(builder.nodes.len(), 2);
728 assert!(builder.nodes.contains_key("0x1000"));
729 assert!(builder.nodes.contains_key("0x2000"));
730
731 let node1 = &builder.nodes["0x1000"];
732 assert_eq!(node1.name, "var1");
733 assert_eq!(node1.type_name, "i32");
734 assert_eq!(node1.size, 1024);
735 assert_eq!(node1.scope, "main");
736 assert!(node1.is_active);
737 assert_eq!(node1.category, VariableCategory::UserVariable);
738 }
739
740 #[test]
741 fn test_add_allocations_with_registry() {
742 let allocations = vec![create_test_allocation(0x1000, 1024, None, None, None)];
743
744 let mut registry = HashMap::new();
745 registry.insert(
746 0x1000,
747 create_test_variable_info("registry_var".to_string(), "u64".to_string()),
748 );
749
750 let builder = VariableRelationshipBuilder::new().add_allocations(&allocations, ®istry);
751
752 assert_eq!(builder.nodes.len(), 1);
753 let node = &builder.nodes["0x1000"];
754 assert_eq!(node.name, "registry_var");
755 assert_eq!(node.type_name, "u64");
756 assert_eq!(node.category, VariableCategory::UserVariable);
757 }
758
759 #[test]
760 fn test_add_allocations_inferred_categories() {
761 let allocations = vec![
762 create_test_allocation(0x1000, 1024, None, Some("Vec<i32>".to_string()), None),
763 create_test_allocation(0x2000, 512, None, Some("Box<String>".to_string()), None),
764 create_test_allocation(
765 0x3000,
766 256,
767 None,
768 Some("HashMap<String, i32>".to_string()),
769 None,
770 ),
771 create_test_allocation(0x4000, 128, None, Some("Rc<Data>".to_string()), None),
772 ];
773
774 let registry = HashMap::new();
775
776 let builder = VariableRelationshipBuilder::new().add_allocations(&allocations, ®istry);
777
778 assert_eq!(builder.nodes.len(), 4);
779
780 let categories: Vec<_> = builder.nodes.values().map(|node| &node.category).collect();
783
784 let has_user_vars = categories
786 .iter()
787 .any(|&cat| *cat == VariableCategory::UserVariable);
788 let has_smart_ptrs = categories
789 .iter()
790 .any(|&cat| *cat == VariableCategory::SmartPointer);
791 let has_collections = categories
792 .iter()
793 .any(|&cat| *cat == VariableCategory::Collection);
794 let has_system_allocs = categories
795 .iter()
796 .any(|&cat| *cat == VariableCategory::SystemAllocation);
797
798 assert!(has_user_vars || has_smart_ptrs || has_collections || has_system_allocs);
800
801 assert!(builder.nodes.contains_key("0x1000"));
803 assert!(builder.nodes.contains_key("0x2000"));
804 assert!(builder.nodes.contains_key("0x3000"));
805 assert!(builder.nodes.contains_key("0x4000"));
806 }
807
808 #[test]
809 fn test_detect_references_smart_pointers() {
810 let allocations = vec![
811 create_smart_pointer_allocation(
812 0x1000,
813 64,
814 "rc1".to_string(),
815 "Rc<i32>".to_string(),
816 SmartPointerType::Rc,
817 0x5000,
818 vec![0x2000, 0x3000],
819 None,
820 ),
821 create_smart_pointer_allocation(
822 0x2000,
823 64,
824 "rc2".to_string(),
825 "Rc<i32>".to_string(),
826 SmartPointerType::Rc,
827 0x5000,
828 vec![],
829 Some(0x1000),
830 ),
831 create_smart_pointer_allocation(
832 0x3000,
833 64,
834 "rc3".to_string(),
835 "Rc<i32>".to_string(),
836 SmartPointerType::Rc,
837 0x5000,
838 vec![],
839 Some(0x1000),
840 ),
841 ];
842
843 let registry = HashMap::new();
844
845 let builder = VariableRelationshipBuilder::new()
846 .add_allocations(&allocations, ®istry)
847 .detect_references();
848
849 assert!(!builder.relationships.is_empty());
851
852 let clone_relationships: Vec<_> = builder
853 .relationships
854 .iter()
855 .filter(|rel| rel.relationship_type == RelationshipType::Clones)
856 .collect();
857
858 assert!(!clone_relationships.is_empty());
859
860 let has_clone_rel = clone_relationships.iter().any(|rel| {
862 rel.source == "0x1000" && (rel.target == "0x2000" || rel.target == "0x3000")
863 });
864
865 assert!(has_clone_rel);
866 }
867
868 #[test]
869 fn test_detect_references_box_ownership() {
870 let allocations = vec![
871 create_smart_pointer_allocation(
872 0x1000,
873 64,
874 "box_ptr".to_string(),
875 "Box<String>".to_string(),
876 SmartPointerType::Box,
877 0x2000,
878 vec![],
879 None,
880 ),
881 create_test_allocation(
882 0x2000,
883 32,
884 Some("data".to_string()),
885 Some("String".to_string()),
886 Some("main".to_string()),
887 ),
888 ];
889
890 let registry = HashMap::new();
891
892 let builder = VariableRelationshipBuilder::new()
893 .add_allocations(&allocations, ®istry)
894 .detect_references();
895
896 let ownership_relationships: Vec<_> = builder
898 .relationships
899 .iter()
900 .filter(|rel| rel.relationship_type == RelationshipType::Owns)
901 .collect();
902
903 assert!(!ownership_relationships.is_empty());
904
905 let has_ownership = ownership_relationships
906 .iter()
907 .any(|rel| rel.source == "0x1000" && rel.target == "0x2000");
908
909 assert!(has_ownership);
910 }
911
912 #[test]
913 fn test_detect_scope_relationships() {
914 let allocations = vec![
915 create_test_allocation(
916 0x1000,
917 1024,
918 Some("var1".to_string()),
919 Some("i32".to_string()),
920 Some("main".to_string()),
921 ),
922 create_test_allocation(
923 0x2000,
924 512,
925 Some("var2".to_string()),
926 Some("String".to_string()),
927 Some("main".to_string()),
928 ),
929 create_test_allocation(
930 0x3000,
931 256,
932 Some("var3".to_string()),
933 Some("f64".to_string()),
934 Some("function".to_string()),
935 ),
936 ];
937
938 let registry = HashMap::new();
939
940 let builder = VariableRelationshipBuilder::new()
941 .add_allocations(&allocations, ®istry)
942 .detect_scope_relationships();
943
944 assert!(!builder.clusters.is_empty());
946
947 let scope_clusters: Vec<_> = builder
948 .clusters
949 .iter()
950 .filter(|cluster| cluster.cluster_type == ClusterType::Scope)
951 .collect();
952
953 assert!(!scope_clusters.is_empty());
954
955 let main_cluster = scope_clusters
957 .iter()
958 .find(|cluster| cluster.id == "scope_main");
959
960 assert!(main_cluster.is_some());
961 let main_cluster = main_cluster.unwrap();
962 assert_eq!(main_cluster.variables.len(), 2);
963 assert!(main_cluster.variables.contains(&"0x1000".to_string()));
964 assert!(main_cluster.variables.contains(&"0x2000".to_string()));
965
966 let containment_relationships: Vec<_> = builder
968 .relationships
969 .iter()
970 .filter(|rel| rel.relationship_type == RelationshipType::Contains)
971 .collect();
972
973 assert!(!containment_relationships.is_empty());
974 }
975
976 #[test]
977 fn test_build_graph_basic() {
978 let allocations = vec![
979 create_test_allocation(
980 0x1000,
981 1024,
982 Some("var1".to_string()),
983 Some("i32".to_string()),
984 Some("main".to_string()),
985 ),
986 create_test_allocation(
987 0x2000,
988 512,
989 Some("var2".to_string()),
990 Some("String".to_string()),
991 Some("main".to_string()),
992 ),
993 ];
994
995 let registry = HashMap::new();
996
997 let graph = VariableRelationshipBuilder::new()
998 .add_allocations(&allocations, ®istry)
999 .detect_scope_relationships()
1000 .build_graph();
1001
1002 assert_eq!(graph.nodes.len(), 2);
1003 assert!(!graph.relationships.is_empty());
1004 assert!(!graph.clusters.is_empty());
1005
1006 assert_eq!(graph.statistics.total_nodes, 2);
1008 assert!(graph.statistics.total_relationships > 0);
1009 assert!(graph.statistics.avg_relationships_per_node >= 0.0);
1010
1011 assert!(graph.metadata.contains_key("build_timestamp"));
1013 }
1014
1015 #[test]
1016 fn test_calculate_statistics() {
1017 let allocations = vec![
1018 create_test_allocation(
1019 0x1000,
1020 1024,
1021 Some("var1".to_string()),
1022 Some("i32".to_string()),
1023 Some("main".to_string()),
1024 ),
1025 create_test_allocation(
1026 0x2000,
1027 512,
1028 Some("var2".to_string()),
1029 Some("String".to_string()),
1030 Some("main".to_string()),
1031 ),
1032 create_test_allocation(
1033 0x3000,
1034 256,
1035 Some("isolated".to_string()),
1036 Some("f64".to_string()),
1037 Some("other".to_string()),
1038 ),
1039 ];
1040
1041 let registry = HashMap::new();
1042
1043 let graph = VariableRelationshipBuilder::new()
1044 .add_allocations(&allocations, ®istry)
1045 .detect_scope_relationships()
1046 .build_graph();
1047
1048 let stats = &graph.statistics;
1049 assert_eq!(stats.total_nodes, 3);
1050 assert!(stats.total_relationships > 0);
1051 assert!(stats.largest_cluster_size >= 2); assert!(stats.isolated_nodes <= 1); assert!(stats.avg_relationships_per_node >= 0.0);
1054 }
1055
1056 #[test]
1057 fn test_smart_pointer_info_conversion() {
1058 let allocations = vec![create_smart_pointer_allocation(
1059 0x1000,
1060 64,
1061 "rc_ptr".to_string(),
1062 "Rc<Data>".to_string(),
1063 SmartPointerType::Rc,
1064 0x2000,
1065 vec![0x3000],
1066 Some(0x4000),
1067 )];
1068
1069 let registry = HashMap::new();
1070
1071 let builder = VariableRelationshipBuilder::new().add_allocations(&allocations, ®istry);
1072
1073 let node = &builder.nodes["0x1000"];
1074 assert!(node.smart_pointer_info.is_some());
1075
1076 let smart_ptr_info = node.smart_pointer_info.as_ref().unwrap();
1077 assert_eq!(smart_ptr_info.pointer_type, "Rc");
1078 assert_eq!(smart_ptr_info.ref_count, Some(1));
1079 assert_eq!(smart_ptr_info.data_ptr, Some(0x2000));
1080 assert_eq!(smart_ptr_info.clones, vec![0x3000]);
1081 assert_eq!(smart_ptr_info.cloned_from, Some(0x4000));
1082 }
1083
1084 #[test]
1085 fn test_layout_hint_serialization() {
1086 let layout_hint = LayoutHint {
1087 x: 10.0,
1088 y: 20.0,
1089 width: Some(100.0),
1090 height: Some(50.0),
1091 };
1092
1093 let serialized = serde_json::to_string(&layout_hint).expect("Failed to serialize");
1094 let deserialized: LayoutHint =
1095 serde_json::from_str(&serialized).expect("Failed to deserialize");
1096
1097 assert_eq!(deserialized.x, 10.0);
1098 assert_eq!(deserialized.y, 20.0);
1099 assert_eq!(deserialized.width, Some(100.0));
1100 assert_eq!(deserialized.height, Some(50.0));
1101 }
1102
1103 #[test]
1104 fn test_variable_node_serialization() {
1105 let node = VariableNode {
1106 id: "0x1000".to_string(),
1107 name: "test_var".to_string(),
1108 type_name: "i32".to_string(),
1109 size: 4,
1110 scope: "main".to_string(),
1111 is_active: true,
1112 category: VariableCategory::UserVariable,
1113 smart_pointer_info: None,
1114 created_at: 1000,
1115 destroyed_at: None,
1116 };
1117
1118 let serialized = serde_json::to_string(&node).expect("Failed to serialize");
1119 let deserialized: VariableNode =
1120 serde_json::from_str(&serialized).expect("Failed to deserialize");
1121
1122 assert_eq!(deserialized.id, "0x1000");
1123 assert_eq!(deserialized.name, "test_var");
1124 assert_eq!(deserialized.type_name, "i32");
1125 assert_eq!(deserialized.size, 4);
1126 assert!(deserialized.is_active);
1127 }
1128
1129 #[test]
1130 fn test_variable_relationship_serialization() {
1131 let mut metadata = HashMap::new();
1132 metadata.insert(
1133 "test_key".to_string(),
1134 serde_json::Value::String("test_value".to_string()),
1135 );
1136
1137 let relationship = VariableRelationship {
1138 source: "0x1000".to_string(),
1139 target: "0x2000".to_string(),
1140 relationship_type: RelationshipType::References,
1141 weight: 0.8,
1142 metadata,
1143 };
1144
1145 let serialized = serde_json::to_string(&relationship).expect("Failed to serialize");
1146 let deserialized: VariableRelationship =
1147 serde_json::from_str(&serialized).expect("Failed to deserialize");
1148
1149 assert_eq!(deserialized.source, "0x1000");
1150 assert_eq!(deserialized.target, "0x2000");
1151 assert_eq!(deserialized.relationship_type, RelationshipType::References);
1152 assert_eq!(deserialized.weight, 0.8);
1153 assert!(deserialized.metadata.contains_key("test_key"));
1154 }
1155
1156 #[test]
1157 fn test_build_variable_relationship_graph_function() {
1158 let allocations = vec![
1159 create_test_allocation(
1160 0x1000,
1161 1024,
1162 Some("var1".to_string()),
1163 Some("i32".to_string()),
1164 Some("main".to_string()),
1165 ),
1166 create_test_allocation(
1167 0x2000,
1168 512,
1169 Some("var2".to_string()),
1170 Some("String".to_string()),
1171 Some("main".to_string()),
1172 ),
1173 ];
1174
1175 let registry = HashMap::new();
1176
1177 let result = build_variable_relationship_graph(&allocations, ®istry);
1178 assert!(result.is_ok());
1179
1180 let graph = result.unwrap();
1181 assert_eq!(graph.nodes.len(), 2);
1182 assert!(!graph.relationships.is_empty());
1183 assert!(!graph.clusters.is_empty());
1184 }
1185
1186 #[test]
1187 fn test_comprehensive_workflow() {
1188 let allocations = vec![
1190 create_test_allocation(
1192 0x1000,
1193 1024,
1194 Some("main_var1".to_string()),
1195 Some("i32".to_string()),
1196 Some("main".to_string()),
1197 ),
1198 create_test_allocation(
1199 0x2000,
1200 512,
1201 Some("main_var2".to_string()),
1202 Some("String".to_string()),
1203 Some("main".to_string()),
1204 ),
1205 create_test_allocation(
1207 0x3000,
1208 256,
1209 Some("func_var".to_string()),
1210 Some("f64".to_string()),
1211 Some("function".to_string()),
1212 ),
1213 create_smart_pointer_allocation(
1215 0x4000,
1216 64,
1217 "rc1".to_string(),
1218 "Rc<Data>".to_string(),
1219 SmartPointerType::Rc,
1220 0x6000,
1221 vec![0x5000],
1222 None,
1223 ),
1224 create_smart_pointer_allocation(
1225 0x5000,
1226 64,
1227 "rc2".to_string(),
1228 "Rc<Data>".to_string(),
1229 SmartPointerType::Rc,
1230 0x6000,
1231 vec![],
1232 Some(0x4000),
1233 ),
1234 create_smart_pointer_allocation(
1236 0x7000,
1237 64,
1238 "box_ptr".to_string(),
1239 "Box<String>".to_string(),
1240 SmartPointerType::Box,
1241 0x8000,
1242 vec![],
1243 None,
1244 ),
1245 create_test_allocation(
1246 0x8000,
1247 32,
1248 Some("boxed_data".to_string()),
1249 Some("String".to_string()),
1250 Some("main".to_string()),
1251 ),
1252 create_test_allocation(
1254 0x9000,
1255 128,
1256 Some("vec_data".to_string()),
1257 Some("Vec<i32>".to_string()),
1258 Some("main".to_string()),
1259 ),
1260 ];
1261
1262 let mut registry = HashMap::new();
1263 registry.insert(
1264 0x1000,
1265 create_test_variable_info("registry_main_var".to_string(), "u32".to_string()),
1266 );
1267
1268 let graph = VariableRelationshipBuilder::new()
1269 .add_allocations(&allocations, ®istry)
1270 .detect_references()
1271 .detect_scope_relationships()
1272 .detect_circular_references()
1273 .build_graph();
1274
1275 assert_eq!(graph.nodes.len(), 8);
1277 assert!(!graph.relationships.is_empty());
1278 assert!(!graph.clusters.is_empty());
1279
1280 let has_clones = graph
1282 .relationships
1283 .iter()
1284 .any(|rel| rel.relationship_type == RelationshipType::Clones);
1285 let has_ownership = graph
1286 .relationships
1287 .iter()
1288 .any(|rel| rel.relationship_type == RelationshipType::Owns);
1289 let has_containment = graph
1290 .relationships
1291 .iter()
1292 .any(|rel| rel.relationship_type == RelationshipType::Contains);
1293
1294 assert!(has_clones || has_ownership || has_containment);
1295
1296 let mut categories = Vec::new();
1298
1299 for node in &graph.nodes {
1301 if !categories.contains(&&node.category) {
1302 categories.push(&node.category);
1303 }
1304 }
1305
1306 assert!(
1308 !categories.is_empty(),
1309 "Expected at least one variable category"
1310 );
1311
1312 let has_user_vars = categories.contains(&&VariableCategory::UserVariable);
1314 assert!(has_user_vars, "Expected at least one user variable");
1315
1316 let stats = &graph.statistics;
1318 assert_eq!(stats.total_nodes, 8);
1319 assert!(stats.total_relationships > 0);
1320 assert!(stats.avg_relationships_per_node >= 0.0);
1321
1322 assert!(graph.metadata.contains_key("build_timestamp"));
1324
1325 let scope_clusters: Vec<_> = graph
1327 .clusters
1328 .iter()
1329 .filter(|cluster| cluster.cluster_type == ClusterType::Scope)
1330 .collect();
1331
1332 assert!(!scope_clusters.is_empty());
1333
1334 let main_cluster = scope_clusters
1336 .iter()
1337 .find(|cluster| cluster.variables.len() > 1);
1338
1339 assert!(main_cluster.is_some());
1340 }
1341}