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 }
635 }
636
637 #[test]
638 fn test_relationship_type_serialization() {
639 let relationship_types = vec![
640 RelationshipType::References,
641 RelationshipType::Owns,
642 RelationshipType::Clones,
643 RelationshipType::Contains,
644 RelationshipType::DependsOn,
645 ];
646
647 for rel_type in relationship_types {
648 let serialized = serde_json::to_string(&rel_type).expect("Failed to serialize");
649 let _deserialized: RelationshipType =
650 serde_json::from_str(&serialized).expect("Failed to deserialize");
651 }
652 }
653
654 #[test]
655 fn test_variable_category_serialization() {
656 let categories = vec![
657 VariableCategory::UserVariable,
658 VariableCategory::SystemAllocation,
659 VariableCategory::SmartPointer,
660 VariableCategory::Collection,
661 ];
662
663 for category in categories {
664 let serialized = serde_json::to_string(&category).expect("Failed to serialize");
665 let _deserialized: VariableCategory =
666 serde_json::from_str(&serialized).expect("Failed to deserialize");
667 }
668 }
669
670 #[test]
671 fn test_cluster_type_serialization() {
672 let cluster_types = vec![
673 ClusterType::Scope,
674 ClusterType::Type,
675 ClusterType::Lifetime,
676 ClusterType::SmartPointerGroup,
677 ];
678
679 for cluster_type in cluster_types {
680 let serialized = serde_json::to_string(&cluster_type).expect("Failed to serialize");
681 let _deserialized: ClusterType =
682 serde_json::from_str(&serialized).expect("Failed to deserialize");
683 }
684 }
685
686 #[test]
687 fn test_variable_relationship_builder_creation() {
688 let builder = VariableRelationshipBuilder::new();
689 assert!(builder.nodes.is_empty());
690 assert!(builder.relationships.is_empty());
691 assert!(builder.clusters.is_empty());
692 }
693
694 #[test]
695 fn test_variable_relationship_builder_default() {
696 let builder = VariableRelationshipBuilder::default();
697 assert!(builder.nodes.is_empty());
698 assert!(builder.relationships.is_empty());
699 assert!(builder.clusters.is_empty());
700 }
701
702 #[test]
703 fn test_add_allocations_basic() {
704 let allocations = vec![
705 create_test_allocation(
706 0x1000,
707 1024,
708 Some("var1".to_string()),
709 Some("i32".to_string()),
710 Some("main".to_string()),
711 ),
712 create_test_allocation(
713 0x2000,
714 512,
715 Some("var2".to_string()),
716 Some("String".to_string()),
717 Some("main".to_string()),
718 ),
719 ];
720
721 let registry = HashMap::new();
722
723 let builder = VariableRelationshipBuilder::new().add_allocations(&allocations, ®istry);
724
725 assert_eq!(builder.nodes.len(), 2);
726 assert!(builder.nodes.contains_key("0x1000"));
727 assert!(builder.nodes.contains_key("0x2000"));
728
729 let node1 = &builder.nodes["0x1000"];
730 assert_eq!(node1.name, "var1");
731 assert_eq!(node1.type_name, "i32");
732 assert_eq!(node1.size, 1024);
733 assert_eq!(node1.scope, "main");
734 assert!(node1.is_active);
735 assert_eq!(node1.category, VariableCategory::UserVariable);
736 }
737
738 #[test]
739 fn test_add_allocations_with_registry() {
740 let allocations = vec![create_test_allocation(0x1000, 1024, None, None, None)];
741
742 let mut registry = HashMap::new();
743 registry.insert(
744 0x1000,
745 create_test_variable_info("registry_var".to_string(), "u64".to_string()),
746 );
747
748 let builder = VariableRelationshipBuilder::new().add_allocations(&allocations, ®istry);
749
750 assert_eq!(builder.nodes.len(), 1);
751 let node = &builder.nodes["0x1000"];
752 assert_eq!(node.name, "registry_var");
753 assert_eq!(node.type_name, "u64");
754 assert_eq!(node.category, VariableCategory::UserVariable);
755 }
756
757 #[test]
758 fn test_add_allocations_inferred_categories() {
759 let allocations = vec![
760 create_test_allocation(0x1000, 1024, None, Some("Vec<i32>".to_string()), None),
761 create_test_allocation(0x2000, 512, None, Some("Box<String>".to_string()), None),
762 create_test_allocation(
763 0x3000,
764 256,
765 None,
766 Some("HashMap<String, i32>".to_string()),
767 None,
768 ),
769 create_test_allocation(0x4000, 128, None, Some("Rc<Data>".to_string()), None),
770 ];
771
772 let registry = HashMap::new();
773
774 let builder = VariableRelationshipBuilder::new().add_allocations(&allocations, ®istry);
775
776 assert_eq!(builder.nodes.len(), 4);
777
778 let categories: Vec<_> = builder.nodes.values().map(|node| &node.category).collect();
781
782 let has_user_vars = categories
784 .iter()
785 .any(|&cat| *cat == VariableCategory::UserVariable);
786 let has_smart_ptrs = categories
787 .iter()
788 .any(|&cat| *cat == VariableCategory::SmartPointer);
789 let has_collections = categories
790 .iter()
791 .any(|&cat| *cat == VariableCategory::Collection);
792 let has_system_allocs = categories
793 .iter()
794 .any(|&cat| *cat == VariableCategory::SystemAllocation);
795
796 assert!(has_user_vars || has_smart_ptrs || has_collections || has_system_allocs);
798
799 assert!(builder.nodes.contains_key("0x1000"));
801 assert!(builder.nodes.contains_key("0x2000"));
802 assert!(builder.nodes.contains_key("0x3000"));
803 assert!(builder.nodes.contains_key("0x4000"));
804 }
805
806 #[test]
807 fn test_detect_references_smart_pointers() {
808 let allocations = vec![
809 create_smart_pointer_allocation(
810 0x1000,
811 64,
812 "rc1".to_string(),
813 "Rc<i32>".to_string(),
814 SmartPointerType::Rc,
815 0x5000,
816 vec![0x2000, 0x3000],
817 None,
818 ),
819 create_smart_pointer_allocation(
820 0x2000,
821 64,
822 "rc2".to_string(),
823 "Rc<i32>".to_string(),
824 SmartPointerType::Rc,
825 0x5000,
826 vec![],
827 Some(0x1000),
828 ),
829 create_smart_pointer_allocation(
830 0x3000,
831 64,
832 "rc3".to_string(),
833 "Rc<i32>".to_string(),
834 SmartPointerType::Rc,
835 0x5000,
836 vec![],
837 Some(0x1000),
838 ),
839 ];
840
841 let registry = HashMap::new();
842
843 let builder = VariableRelationshipBuilder::new()
844 .add_allocations(&allocations, ®istry)
845 .detect_references();
846
847 assert!(!builder.relationships.is_empty());
849
850 let clone_relationships: Vec<_> = builder
851 .relationships
852 .iter()
853 .filter(|rel| rel.relationship_type == RelationshipType::Clones)
854 .collect();
855
856 assert!(!clone_relationships.is_empty());
857
858 let has_clone_rel = clone_relationships.iter().any(|rel| {
860 rel.source == "0x1000" && (rel.target == "0x2000" || rel.target == "0x3000")
861 });
862
863 assert!(has_clone_rel);
864 }
865
866 #[test]
867 fn test_detect_references_box_ownership() {
868 let allocations = vec![
869 create_smart_pointer_allocation(
870 0x1000,
871 64,
872 "box_ptr".to_string(),
873 "Box<String>".to_string(),
874 SmartPointerType::Box,
875 0x2000,
876 vec![],
877 None,
878 ),
879 create_test_allocation(
880 0x2000,
881 32,
882 Some("data".to_string()),
883 Some("String".to_string()),
884 Some("main".to_string()),
885 ),
886 ];
887
888 let registry = HashMap::new();
889
890 let builder = VariableRelationshipBuilder::new()
891 .add_allocations(&allocations, ®istry)
892 .detect_references();
893
894 let ownership_relationships: Vec<_> = builder
896 .relationships
897 .iter()
898 .filter(|rel| rel.relationship_type == RelationshipType::Owns)
899 .collect();
900
901 assert!(!ownership_relationships.is_empty());
902
903 let has_ownership = ownership_relationships
904 .iter()
905 .any(|rel| rel.source == "0x1000" && rel.target == "0x2000");
906
907 assert!(has_ownership);
908 }
909
910 #[test]
911 fn test_detect_scope_relationships() {
912 let allocations = vec![
913 create_test_allocation(
914 0x1000,
915 1024,
916 Some("var1".to_string()),
917 Some("i32".to_string()),
918 Some("main".to_string()),
919 ),
920 create_test_allocation(
921 0x2000,
922 512,
923 Some("var2".to_string()),
924 Some("String".to_string()),
925 Some("main".to_string()),
926 ),
927 create_test_allocation(
928 0x3000,
929 256,
930 Some("var3".to_string()),
931 Some("f64".to_string()),
932 Some("function".to_string()),
933 ),
934 ];
935
936 let registry = HashMap::new();
937
938 let builder = VariableRelationshipBuilder::new()
939 .add_allocations(&allocations, ®istry)
940 .detect_scope_relationships();
941
942 assert!(!builder.clusters.is_empty());
944
945 let scope_clusters: Vec<_> = builder
946 .clusters
947 .iter()
948 .filter(|cluster| cluster.cluster_type == ClusterType::Scope)
949 .collect();
950
951 assert!(!scope_clusters.is_empty());
952
953 let main_cluster = scope_clusters
955 .iter()
956 .find(|cluster| cluster.id == "scope_main");
957
958 assert!(main_cluster.is_some());
959 let main_cluster = main_cluster.unwrap();
960 assert_eq!(main_cluster.variables.len(), 2);
961 assert!(main_cluster.variables.contains(&"0x1000".to_string()));
962 assert!(main_cluster.variables.contains(&"0x2000".to_string()));
963
964 let containment_relationships: Vec<_> = builder
966 .relationships
967 .iter()
968 .filter(|rel| rel.relationship_type == RelationshipType::Contains)
969 .collect();
970
971 assert!(!containment_relationships.is_empty());
972 }
973
974 #[test]
975 fn test_build_graph_basic() {
976 let allocations = vec![
977 create_test_allocation(
978 0x1000,
979 1024,
980 Some("var1".to_string()),
981 Some("i32".to_string()),
982 Some("main".to_string()),
983 ),
984 create_test_allocation(
985 0x2000,
986 512,
987 Some("var2".to_string()),
988 Some("String".to_string()),
989 Some("main".to_string()),
990 ),
991 ];
992
993 let registry = HashMap::new();
994
995 let graph = VariableRelationshipBuilder::new()
996 .add_allocations(&allocations, ®istry)
997 .detect_scope_relationships()
998 .build_graph();
999
1000 assert_eq!(graph.nodes.len(), 2);
1001 assert!(!graph.relationships.is_empty());
1002 assert!(!graph.clusters.is_empty());
1003
1004 assert_eq!(graph.statistics.total_nodes, 2);
1006 assert!(graph.statistics.total_relationships > 0);
1007 assert!(graph.statistics.avg_relationships_per_node >= 0.0);
1008
1009 assert!(graph.metadata.contains_key("build_timestamp"));
1011 }
1012
1013 #[test]
1014 fn test_calculate_statistics() {
1015 let allocations = vec![
1016 create_test_allocation(
1017 0x1000,
1018 1024,
1019 Some("var1".to_string()),
1020 Some("i32".to_string()),
1021 Some("main".to_string()),
1022 ),
1023 create_test_allocation(
1024 0x2000,
1025 512,
1026 Some("var2".to_string()),
1027 Some("String".to_string()),
1028 Some("main".to_string()),
1029 ),
1030 create_test_allocation(
1031 0x3000,
1032 256,
1033 Some("isolated".to_string()),
1034 Some("f64".to_string()),
1035 Some("other".to_string()),
1036 ),
1037 ];
1038
1039 let registry = HashMap::new();
1040
1041 let graph = VariableRelationshipBuilder::new()
1042 .add_allocations(&allocations, ®istry)
1043 .detect_scope_relationships()
1044 .build_graph();
1045
1046 let stats = &graph.statistics;
1047 assert_eq!(stats.total_nodes, 3);
1048 assert!(stats.total_relationships > 0);
1049 assert!(stats.largest_cluster_size >= 2); assert!(stats.isolated_nodes <= 1); assert!(stats.avg_relationships_per_node >= 0.0);
1052 }
1053
1054 #[test]
1055 fn test_smart_pointer_info_conversion() {
1056 let allocations = vec![create_smart_pointer_allocation(
1057 0x1000,
1058 64,
1059 "rc_ptr".to_string(),
1060 "Rc<Data>".to_string(),
1061 SmartPointerType::Rc,
1062 0x2000,
1063 vec![0x3000],
1064 Some(0x4000),
1065 )];
1066
1067 let registry = HashMap::new();
1068
1069 let builder = VariableRelationshipBuilder::new().add_allocations(&allocations, ®istry);
1070
1071 let node = &builder.nodes["0x1000"];
1072 assert!(node.smart_pointer_info.is_some());
1073
1074 let smart_ptr_info = node.smart_pointer_info.as_ref().unwrap();
1075 assert_eq!(smart_ptr_info.pointer_type, "Rc");
1076 assert_eq!(smart_ptr_info.ref_count, Some(1));
1077 assert_eq!(smart_ptr_info.data_ptr, Some(0x2000));
1078 assert_eq!(smart_ptr_info.clones, vec![0x3000]);
1079 assert_eq!(smart_ptr_info.cloned_from, Some(0x4000));
1080 }
1081
1082 #[test]
1083 fn test_layout_hint_serialization() {
1084 let layout_hint = LayoutHint {
1085 x: 10.0,
1086 y: 20.0,
1087 width: Some(100.0),
1088 height: Some(50.0),
1089 };
1090
1091 let serialized = serde_json::to_string(&layout_hint).expect("Failed to serialize");
1092 let deserialized: LayoutHint =
1093 serde_json::from_str(&serialized).expect("Failed to deserialize");
1094
1095 assert_eq!(deserialized.x, 10.0);
1096 assert_eq!(deserialized.y, 20.0);
1097 assert_eq!(deserialized.width, Some(100.0));
1098 assert_eq!(deserialized.height, Some(50.0));
1099 }
1100
1101 #[test]
1102 fn test_variable_node_serialization() {
1103 let node = VariableNode {
1104 id: "0x1000".to_string(),
1105 name: "test_var".to_string(),
1106 type_name: "i32".to_string(),
1107 size: 4,
1108 scope: "main".to_string(),
1109 is_active: true,
1110 category: VariableCategory::UserVariable,
1111 smart_pointer_info: None,
1112 created_at: 1000,
1113 destroyed_at: None,
1114 };
1115
1116 let serialized = serde_json::to_string(&node).expect("Failed to serialize");
1117 let deserialized: VariableNode =
1118 serde_json::from_str(&serialized).expect("Failed to deserialize");
1119
1120 assert_eq!(deserialized.id, "0x1000");
1121 assert_eq!(deserialized.name, "test_var");
1122 assert_eq!(deserialized.type_name, "i32");
1123 assert_eq!(deserialized.size, 4);
1124 assert!(deserialized.is_active);
1125 }
1126
1127 #[test]
1128 fn test_variable_relationship_serialization() {
1129 let mut metadata = HashMap::new();
1130 metadata.insert(
1131 "test_key".to_string(),
1132 serde_json::Value::String("test_value".to_string()),
1133 );
1134
1135 let relationship = VariableRelationship {
1136 source: "0x1000".to_string(),
1137 target: "0x2000".to_string(),
1138 relationship_type: RelationshipType::References,
1139 weight: 0.8,
1140 metadata,
1141 };
1142
1143 let serialized = serde_json::to_string(&relationship).expect("Failed to serialize");
1144 let deserialized: VariableRelationship =
1145 serde_json::from_str(&serialized).expect("Failed to deserialize");
1146
1147 assert_eq!(deserialized.source, "0x1000");
1148 assert_eq!(deserialized.target, "0x2000");
1149 assert_eq!(deserialized.relationship_type, RelationshipType::References);
1150 assert_eq!(deserialized.weight, 0.8);
1151 assert!(deserialized.metadata.contains_key("test_key"));
1152 }
1153
1154 #[test]
1155 fn test_build_variable_relationship_graph_function() {
1156 let allocations = vec![
1157 create_test_allocation(
1158 0x1000,
1159 1024,
1160 Some("var1".to_string()),
1161 Some("i32".to_string()),
1162 Some("main".to_string()),
1163 ),
1164 create_test_allocation(
1165 0x2000,
1166 512,
1167 Some("var2".to_string()),
1168 Some("String".to_string()),
1169 Some("main".to_string()),
1170 ),
1171 ];
1172
1173 let registry = HashMap::new();
1174
1175 let result = build_variable_relationship_graph(&allocations, ®istry);
1176 assert!(result.is_ok());
1177
1178 let graph = result.unwrap();
1179 assert_eq!(graph.nodes.len(), 2);
1180 assert!(!graph.relationships.is_empty());
1181 assert!(!graph.clusters.is_empty());
1182 }
1183
1184 #[test]
1185 fn test_comprehensive_workflow() {
1186 let allocations = vec![
1188 create_test_allocation(
1190 0x1000,
1191 1024,
1192 Some("main_var1".to_string()),
1193 Some("i32".to_string()),
1194 Some("main".to_string()),
1195 ),
1196 create_test_allocation(
1197 0x2000,
1198 512,
1199 Some("main_var2".to_string()),
1200 Some("String".to_string()),
1201 Some("main".to_string()),
1202 ),
1203 create_test_allocation(
1205 0x3000,
1206 256,
1207 Some("func_var".to_string()),
1208 Some("f64".to_string()),
1209 Some("function".to_string()),
1210 ),
1211 create_smart_pointer_allocation(
1213 0x4000,
1214 64,
1215 "rc1".to_string(),
1216 "Rc<Data>".to_string(),
1217 SmartPointerType::Rc,
1218 0x6000,
1219 vec![0x5000],
1220 None,
1221 ),
1222 create_smart_pointer_allocation(
1223 0x5000,
1224 64,
1225 "rc2".to_string(),
1226 "Rc<Data>".to_string(),
1227 SmartPointerType::Rc,
1228 0x6000,
1229 vec![],
1230 Some(0x4000),
1231 ),
1232 create_smart_pointer_allocation(
1234 0x7000,
1235 64,
1236 "box_ptr".to_string(),
1237 "Box<String>".to_string(),
1238 SmartPointerType::Box,
1239 0x8000,
1240 vec![],
1241 None,
1242 ),
1243 create_test_allocation(
1244 0x8000,
1245 32,
1246 Some("boxed_data".to_string()),
1247 Some("String".to_string()),
1248 Some("main".to_string()),
1249 ),
1250 create_test_allocation(
1252 0x9000,
1253 128,
1254 Some("vec_data".to_string()),
1255 Some("Vec<i32>".to_string()),
1256 Some("main".to_string()),
1257 ),
1258 ];
1259
1260 let mut registry = HashMap::new();
1261 registry.insert(
1262 0x1000,
1263 create_test_variable_info("registry_main_var".to_string(), "u32".to_string()),
1264 );
1265
1266 let graph = VariableRelationshipBuilder::new()
1267 .add_allocations(&allocations, ®istry)
1268 .detect_references()
1269 .detect_scope_relationships()
1270 .detect_circular_references()
1271 .build_graph();
1272
1273 assert_eq!(graph.nodes.len(), 8);
1275 assert!(!graph.relationships.is_empty());
1276 assert!(!graph.clusters.is_empty());
1277
1278 let has_clones = graph
1280 .relationships
1281 .iter()
1282 .any(|rel| rel.relationship_type == RelationshipType::Clones);
1283 let has_ownership = graph
1284 .relationships
1285 .iter()
1286 .any(|rel| rel.relationship_type == RelationshipType::Owns);
1287 let has_containment = graph
1288 .relationships
1289 .iter()
1290 .any(|rel| rel.relationship_type == RelationshipType::Contains);
1291
1292 assert!(has_clones || has_ownership || has_containment);
1293
1294 let mut categories = Vec::new();
1296
1297 for node in &graph.nodes {
1299 if !categories.contains(&&node.category) {
1300 categories.push(&node.category);
1301 }
1302 }
1303
1304 assert!(
1306 !categories.is_empty(),
1307 "Expected at least one variable category"
1308 );
1309
1310 let has_user_vars = categories.contains(&&VariableCategory::UserVariable);
1312 assert!(has_user_vars, "Expected at least one user variable");
1313
1314 let stats = &graph.statistics;
1316 assert_eq!(stats.total_nodes, 8);
1317 assert!(stats.total_relationships > 0);
1318 assert!(stats.avg_relationships_per_node >= 0.0);
1319
1320 assert!(graph.metadata.contains_key("build_timestamp"));
1322
1323 let scope_clusters: Vec<_> = graph
1325 .clusters
1326 .iter()
1327 .filter(|cluster| cluster.cluster_type == ClusterType::Scope)
1328 .collect();
1329
1330 assert!(!scope_clusters.is_empty());
1331
1332 let main_cluster = scope_clusters
1334 .iter()
1335 .find(|cluster| cluster.variables.len() > 1);
1336
1337 assert!(main_cluster.is_some());
1338 }
1339}