memscope_rs/export/
analysis_engine.rs

1//! Unified analysis engine for consistent data processing across different export formats
2//!
3//! This module provides a trait-based architecture that ensures JSON and binary exports
4//! use the same analysis logic, preventing data inconsistencies.
5
6use crate::core::types::AllocationInfo;
7use serde_json::Value;
8use std::error::Error;
9use std::fmt;
10
11/// Analysis data container that can be serialized to different formats
12#[derive(Debug, Clone)]
13pub struct AnalysisData {
14    /// The analyzed data as a JSON value (can be converted to other formats)
15    pub data: Value,
16    /// Metadata about the analysis
17    pub metadata: AnalysisMetadata,
18}
19
20/// Metadata about the analysis process
21#[derive(Debug, Clone)]
22pub struct AnalysisMetadata {
23    /// Type of analysis performed
24    pub analysis_type: String,
25    /// Timestamp when analysis was performed
26    pub timestamp: u64,
27    /// Number of allocations analyzed
28    pub total_allocations: usize,
29    /// Optimization level used
30    pub optimization_level: String,
31}
32
33/// Errors that can occur during analysis
34#[derive(Debug)]
35pub enum AnalysisError {
36    /// Data processing error
37    ProcessingError(String),
38    /// Serialization error
39    SerializationError(String),
40    /// Invalid input data
41    InvalidData(String),
42}
43
44impl fmt::Display for AnalysisError {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        match self {
47            AnalysisError::ProcessingError(msg) => write!(f, "Processing error: {msg}"),
48            AnalysisError::SerializationError(msg) => write!(f, "Serialization error: {msg}"),
49            AnalysisError::InvalidData(msg) => write!(f, "Invalid data: {msg}"),
50        }
51    }
52}
53
54impl Error for AnalysisError {}
55
56/// Unified analysis engine trait for consistent data processing
57///
58/// This trait ensures that all export formats (JSON, binary, etc.) use the same
59/// analysis logic, preventing data inconsistencies between different export methods.
60pub trait AnalysisEngine {
61    /// Create memory analysis data
62    ///
63    /// Analyzes memory allocation patterns, sizes, and basic statistics
64    fn create_memory_analysis(
65        &self,
66        allocations: &[AllocationInfo],
67    ) -> Result<AnalysisData, AnalysisError>;
68
69    /// Create lifetime analysis data
70    ///
71    /// Analyzes allocation lifetimes, scope information, and lifecycle events
72    fn create_lifetime_analysis(
73        &self,
74        allocations: &[AllocationInfo],
75    ) -> Result<AnalysisData, AnalysisError>;
76
77    /// Create performance analysis data
78    ///
79    /// Analyzes performance metrics, allocation patterns, and optimization opportunities
80    fn create_performance_analysis(
81        &self,
82        allocations: &[AllocationInfo],
83    ) -> Result<AnalysisData, AnalysisError>;
84
85    /// Create unsafe FFI analysis data
86    ///
87    /// Analyzes unsafe operations, FFI boundaries, and potential safety violations
88    fn create_unsafe_ffi_analysis(
89        &self,
90        allocations: &[AllocationInfo],
91    ) -> Result<AnalysisData, AnalysisError>;
92
93    /// Create complex types analysis data
94    ///
95    /// Analyzes complex type usage, generic instantiations, and type relationships
96    fn create_complex_types_analysis(
97        &self,
98        allocations: &[AllocationInfo],
99    ) -> Result<AnalysisData, AnalysisError>;
100
101    /// Get analysis engine configuration
102    fn get_config(&self) -> &AnalysisConfig;
103}
104
105/// Configuration for the analysis engine
106#[derive(Debug, Clone)]
107pub struct AnalysisConfig {
108    /// Optimization level to use
109    pub optimization_level: OptimizationLevel,
110    /// Whether to enable parallel processing
111    pub parallel_processing: bool,
112    /// Whether to enable enhanced FFI analysis
113    pub enhanced_ffi_analysis: bool,
114    /// Whether to enable security analysis
115    pub security_analysis: bool,
116    /// Batch size for processing
117    pub batch_size: usize,
118}
119
120/// Optimization levels for analysis
121#[derive(Debug, Clone, PartialEq)]
122pub enum OptimizationLevel {
123    /// Low optimization - basic analysis only
124    Low,
125    /// Medium optimization - standard analysis
126    Medium,
127    /// High optimization - comprehensive analysis
128    High,
129    /// Maximum optimization - all features enabled
130    Maximum,
131}
132
133impl Default for AnalysisConfig {
134    fn default() -> Self {
135        Self {
136            optimization_level: OptimizationLevel::High,
137            parallel_processing: true,
138            enhanced_ffi_analysis: true,
139            security_analysis: false,
140            batch_size: 1000,
141        }
142    }
143}
144
145/// Standard implementation of the analysis engine
146///
147/// This implementation uses the existing optimized analysis functions
148/// to ensure consistency with the current JSON export system.
149pub struct StandardAnalysisEngine {
150    config: AnalysisConfig,
151}
152
153impl StandardAnalysisEngine {
154    /// Create a new standard analysis engine with default configuration
155    pub fn new() -> Self {
156        Self {
157            config: AnalysisConfig::default(),
158        }
159    }
160
161    /// Create a new standard analysis engine with custom configuration
162    pub fn with_config(config: AnalysisConfig) -> Self {
163        Self { config }
164    }
165}
166
167impl Default for StandardAnalysisEngine {
168    fn default() -> Self {
169        Self::new()
170    }
171}
172
173impl AnalysisEngine for StandardAnalysisEngine {
174    fn create_memory_analysis(
175        &self,
176        allocations: &[AllocationInfo],
177    ) -> Result<AnalysisData, AnalysisError> {
178        // Create a simple memory analysis since we can't access the private functions
179        // This will be improved once we refactor the optimized_json_export module
180        let total_size: usize = allocations.iter().map(|a| a.size).sum();
181        let avg_size = if !allocations.is_empty() {
182            total_size / allocations.len()
183        } else {
184            0
185        };
186        let max_size = allocations.iter().map(|a| a.size).max().unwrap_or(0);
187        let min_size = allocations.iter().map(|a| a.size).min().unwrap_or(0);
188
189        let allocations_data: Vec<serde_json::Value> = allocations
190            .iter()
191            .map(|alloc| {
192                serde_json::json!({
193                    "ptr": alloc.ptr,
194                    "size": alloc.size,
195                    "var_name": self.infer_var_name(alloc),
196                    "type_name": self.infer_type_name(alloc),
197                    "scope_name": alloc.scope_name.as_deref().unwrap_or("global"),
198                    "thread_id": alloc.thread_id,
199                    "timestamp_alloc": alloc.timestamp_alloc,
200                    "timestamp_dealloc": alloc.timestamp_dealloc.unwrap_or(0),
201                    "is_leaked": alloc.is_leaked,
202                    "borrow_count": alloc.borrow_count,
203                    "lifetime_ms": alloc.lifetime_ms.unwrap_or(0),
204                    "stack_trace": alloc.stack_trace.as_ref().map(|st| st.join(" -> ")).unwrap_or_else(|| "no_stack_trace".to_string())
205                })
206            })
207            .collect();
208
209        let data = serde_json::json!({
210            "allocations": allocations_data,
211            "metadata": {
212                "analysis_type": "integrated_memory_analysis",
213                "export_version": "2.0",
214                "optimization_level": format!("{:?}", self.config.optimization_level),
215                "timestamp": std::time::SystemTime::now()
216                    .duration_since(std::time::UNIX_EPOCH)
217                    .unwrap_or_default()
218                    .as_secs(),
219                "total_allocations_analyzed": allocations.len(),
220                "pipeline_features": {
221                    "enhanced_ffi_analysis": self.config.enhanced_ffi_analysis,
222                    "parallel_processing": self.config.parallel_processing,
223                    "security_analysis": self.config.security_analysis
224                }
225            },
226            "summary": {
227                "total_allocations": allocations.len(),
228                "total_memory": total_size,
229                "average_size": avg_size,
230                "max_size": max_size,
231                "min_size": min_size,
232                "leaked_count": allocations.iter().filter(|a| a.is_leaked).count()
233            }
234        });
235
236        Ok(AnalysisData {
237            data,
238            metadata: AnalysisMetadata {
239                analysis_type: "integrated_memory_analysis".to_string(),
240                timestamp: std::time::SystemTime::now()
241                    .duration_since(std::time::UNIX_EPOCH)
242                    .unwrap_or_default()
243                    .as_secs(),
244                total_allocations: allocations.len(),
245                optimization_level: format!("{:?}", self.config.optimization_level),
246            },
247        })
248    }
249
250    fn create_lifetime_analysis(
251        &self,
252        allocations: &[AllocationInfo],
253    ) -> Result<AnalysisData, AnalysisError> {
254        // Create lifecycle events from allocations
255        let mut lifecycle_events = Vec::new();
256        let mut scope_analysis = std::collections::HashMap::new();
257
258        for alloc in allocations {
259            // Add allocation event
260            lifecycle_events.push(serde_json::json!({
261                "event": "allocation",
262                "ptr": format!("0x{:x}", alloc.ptr),
263                "size": alloc.size,
264                "timestamp": alloc.timestamp_alloc,
265                "var_name": self.infer_var_name(alloc),
266                "type_name": self.infer_type_name(alloc),
267                "scope": alloc.scope_name.as_deref().unwrap_or("global")
268            }));
269
270            // Add deallocation event if available
271            if let Some(dealloc_time) = alloc.timestamp_dealloc {
272                lifecycle_events.push(serde_json::json!({
273                    "event": "deallocation",
274                    "ptr": format!("0x{:x}", alloc.ptr),
275                    "timestamp": dealloc_time,
276                    "var_name": self.infer_var_name(alloc),
277                    "scope": alloc.scope_name.as_deref().unwrap_or("global")
278                }));
279            }
280
281            // Update scope analysis
282            let scope = alloc.scope_name.as_deref().unwrap_or("global");
283            let entry = scope_analysis
284                .entry(scope.to_string())
285                .or_insert((0, 0, Vec::new()));
286            entry.0 += 1; // allocation count
287            entry.1 += alloc.size; // total size
288            entry.2.push(alloc.size); // individual sizes
289        }
290
291        // Convert scope analysis to the expected format
292        let scope_stats: std::collections::HashMap<String, serde_json::Value> = scope_analysis
293            .into_iter()
294            .map(|(scope, (count, total_size, sizes))| {
295                let avg_size = if count > 0 { total_size / count } else { 0 };
296                (
297                    scope,
298                    serde_json::json!({
299                        "allocation_count": count,
300                        "total_size": total_size,
301                        "average_size": avg_size,
302                        "sizes": sizes
303                    }),
304                )
305            })
306            .collect();
307
308        let data = serde_json::json!({
309            "lifecycle_events": lifecycle_events,
310            "scope_analysis": scope_stats,
311            "variable_lifetimes": {},
312            "metadata": {
313                "analysis_type": "integrated_lifetime_analysis",
314                "export_version": "2.0",
315                "optimization_level": format!("{:?}", self.config.optimization_level),
316                "timestamp": std::time::SystemTime::now()
317                    .duration_since(std::time::UNIX_EPOCH)
318                    .unwrap_or_default()
319                    .as_secs(),
320                "total_allocations_analyzed": allocations.len(),
321                "pipeline_features": {
322                    "enhanced_ffi_analysis": self.config.enhanced_ffi_analysis,
323                    "parallel_processing": self.config.parallel_processing
324                }
325            },
326            "summary": {
327                "total_allocations": allocations.len(),
328                "unique_scopes": scope_stats.len(),
329                "total_events": lifecycle_events.len(),
330                "leaked_count": allocations.iter().filter(|a| a.is_leaked).count()
331            }
332        });
333
334        Ok(AnalysisData {
335            data,
336            metadata: AnalysisMetadata {
337                analysis_type: "integrated_lifetime_analysis".to_string(),
338                timestamp: std::time::SystemTime::now()
339                    .duration_since(std::time::UNIX_EPOCH)
340                    .unwrap_or_default()
341                    .as_secs(),
342                total_allocations: allocations.len(),
343                optimization_level: format!("{:?}", self.config.optimization_level),
344            },
345        })
346    }
347
348    fn create_performance_analysis(
349        &self,
350        allocations: &[AllocationInfo],
351    ) -> Result<AnalysisData, AnalysisError> {
352        // Calculate performance metrics
353        let total_size: usize = allocations.iter().map(|a| a.size).sum();
354        let avg_size = if !allocations.is_empty() {
355            total_size / allocations.len()
356        } else {
357            0
358        };
359        let max_size = allocations.iter().map(|a| a.size).max().unwrap_or(0);
360        let min_size = allocations.iter().map(|a| a.size).min().unwrap_or(0);
361
362        // Group by thread for thread analysis
363        let mut thread_stats = std::collections::HashMap::new();
364        for alloc in allocations {
365            let entry = thread_stats
366                .entry(alloc.thread_id.clone())
367                .or_insert((0, 0));
368            entry.0 += 1; // count
369            entry.1 += alloc.size; // total size
370        }
371
372        let thread_analysis: std::collections::HashMap<String, serde_json::Value> = thread_stats
373            .into_iter()
374            .map(|(thread_id, (count, total_size))| {
375                (
376                    thread_id,
377                    serde_json::json!({
378                        "allocation_count": count,
379                        "total_size": total_size,
380                        "average_size": if count > 0 { total_size / count } else { 0 }
381                    }),
382                )
383            })
384            .collect();
385
386        let allocations_data: Vec<serde_json::Value> = allocations
387            .iter()
388            .map(|alloc| {
389                serde_json::json!({
390                    "ptr": alloc.ptr,
391                    "size": alloc.size,
392                    "timestamp_alloc": alloc.timestamp_alloc,
393                    "thread_id": alloc.thread_id,
394                    "borrow_count": alloc.borrow_count,
395                    "var_name": self.infer_var_name(alloc),
396                    "type_name": self.infer_type_name(alloc),
397                    "scope_name": alloc.scope_name.as_deref().unwrap_or("global"),
398                    "is_leaked": alloc.is_leaked,
399                    "lifetime_ms": alloc.lifetime_ms.unwrap_or(0),
400                    "fragmentation_score": 0.0 // Default fragmentation score when analysis is not available
401                })
402            })
403            .collect();
404
405        let data = serde_json::json!({
406            "allocations": allocations_data,
407            "thread_analysis": thread_analysis,
408            "metadata": {
409                "analysis_type": "integrated_performance_analysis",
410                "export_version": "2.0",
411                "optimization_level": format!("{:?}", self.config.optimization_level),
412                "timestamp": std::time::SystemTime::now()
413                    .duration_since(std::time::UNIX_EPOCH)
414                    .unwrap_or_default()
415                    .as_secs(),
416                "total_allocations_analyzed": allocations.len(),
417                "pipeline_features": {
418                    "parallel_processing": self.config.parallel_processing,
419                    "batch_size": self.config.batch_size
420                }
421            },
422            "summary": {
423                "total_allocations": allocations.len(),
424                "total_memory": total_size,
425                "average_size": avg_size,
426                "max_size": max_size,
427                "min_size": min_size,
428                "unique_threads": thread_analysis.len()
429            }
430        });
431
432        Ok(AnalysisData {
433            data,
434            metadata: AnalysisMetadata {
435                analysis_type: "integrated_performance_analysis".to_string(),
436                timestamp: std::time::SystemTime::now()
437                    .duration_since(std::time::UNIX_EPOCH)
438                    .unwrap_or_default()
439                    .as_secs(),
440                total_allocations: allocations.len(),
441                optimization_level: format!("{:?}", self.config.optimization_level),
442            },
443        })
444    }
445
446    fn create_unsafe_ffi_analysis(
447        &self,
448        _allocations: &[AllocationInfo],
449    ) -> Result<AnalysisData, AnalysisError> {
450        use crate::analysis::unsafe_ffi_tracker::get_global_unsafe_ffi_tracker;
451
452        // Get enhanced allocations with real unsafe/FFI data
453        let tracker = get_global_unsafe_ffi_tracker();
454        let enhanced_allocations = tracker.get_enhanced_allocations().map_err(|e| {
455            AnalysisError::ProcessingError(format!("Failed to get enhanced allocations: {e}"))
456        })?;
457
458        // For unsafe/FFI analysis, we want ALL enhanced allocations, not just user-defined ones
459        // because unsafe/FFI operations often don't have variable names
460        let user_enhanced_allocations = enhanced_allocations;
461
462        // Convert to the expected JSON format matching snapshot_unsafe_ffi.json
463        let data = serde_json::to_value(&user_enhanced_allocations).map_err(|e| {
464            AnalysisError::SerializationError(format!(
465                "Failed to serialize enhanced allocations: {e}"
466            ))
467        })?;
468
469        Ok(AnalysisData {
470            data,
471            metadata: AnalysisMetadata {
472                analysis_type: "unsafe_ffi_analysis".to_string(),
473                timestamp: std::time::SystemTime::now()
474                    .duration_since(std::time::UNIX_EPOCH)
475                    .unwrap_or_default()
476                    .as_secs(),
477                total_allocations: user_enhanced_allocations.len(),
478                optimization_level: format!("{:?}", self.config.optimization_level),
479            },
480        })
481    }
482
483    fn create_complex_types_analysis(
484        &self,
485        allocations: &[AllocationInfo],
486    ) -> Result<AnalysisData, AnalysisError> {
487        // Categorize types
488        let mut categorized_types = std::collections::HashMap::new();
489        let mut generic_types = std::collections::HashMap::new();
490
491        for alloc in allocations {
492            let type_name = self.infer_type_name(alloc);
493
494            // Categorize the type
495            let category = if type_name.contains('<') && type_name.contains('>') {
496                "generic"
497            } else if type_name.starts_with("Vec") || type_name.starts_with("HashMap") {
498                "collection"
499            } else if type_name.contains("::") {
500                "module_type"
501            } else {
502                "primitive"
503            };
504
505            let entry = categorized_types
506                .entry(category.to_string())
507                .or_insert(Vec::new());
508            // Debug: Check if we have the enhanced data
509            if alloc.memory_layout.is_some() {
510                tracing::debug!(
511                    "AllocationInfo has memory_layout for {}",
512                    self.infer_var_name(alloc)
513                );
514            }
515            if alloc.generic_info.is_some() {
516                tracing::debug!(
517                    "AllocationInfo has generic_info for {}",
518                    self.infer_var_name(alloc)
519                );
520            }
521
522            // Manually serialize to avoid potential serde issues
523            let mut json_obj = serde_json::Map::new();
524            json_obj.insert(
525                "ptr".to_string(),
526                serde_json::Value::String(format!("0x{:x}", alloc.ptr)),
527            );
528            json_obj.insert(
529                "size".to_string(),
530                serde_json::Value::Number(serde_json::Number::from(alloc.size)),
531            );
532            json_obj.insert(
533                "var_name".to_string(),
534                serde_json::Value::String(self.infer_var_name(alloc)),
535            );
536            json_obj.insert(
537                "type_name".to_string(),
538                serde_json::Value::String(type_name.to_string()),
539            );
540
541            // Serialize complex fields with default values instead of null
542            json_obj.insert(
543                "smart_pointer_info".to_string(),
544                if let Some(ref info) = alloc.smart_pointer_info {
545                    serde_json::to_value(info)
546                        .unwrap_or_else(|_| serde_json::json!({"type": "unknown", "ref_count": 0}))
547                } else {
548                    serde_json::json!({"type": "none", "ref_count": 0})
549                },
550            );
551
552            json_obj.insert(
553                "memory_layout".to_string(),
554                if let Some(ref layout) = alloc.memory_layout {
555                    match serde_json::to_value(layout) {
556                        Ok(value) => {
557                            tracing::debug!(
558                                "Successfully serialized memory_layout for {}",
559                                self.infer_var_name(alloc)
560                            );
561                            value
562                        }
563                        Err(e) => {
564                            tracing::debug!(
565                                "Failed to serialize memory_layout for {}: {}",
566                                self.infer_var_name(alloc),
567                                e
568                            );
569                            serde_json::json!({"layout_type": "unknown", "size": alloc.size, "alignment": 1})
570                        }
571                    }
572                } else {
573                    serde_json::json!({"layout_type": "default", "size": alloc.size, "alignment": 1})
574                },
575            );
576
577            json_obj.insert(
578                "generic_info".to_string(),
579                if let Some(ref info) = alloc.generic_info {
580                    match serde_json::to_value(info) {
581                        Ok(value) => {
582                            tracing::debug!(
583                                "Successfully serialized generic_info for {}",
584                                self.infer_var_name(alloc)
585                            );
586                            value
587                        }
588                        Err(e) => {
589                            tracing::debug!(
590                                "Failed to serialize generic_info for {}: {}",
591                                self.infer_var_name(alloc),
592                                e
593                            );
594                            serde_json::json!({"generic_type": "none", "type_parameters": []})
595                        }
596                    }
597                } else {
598                    serde_json::json!({"generic_type": "none", "type_parameters": []})
599                },
600            );
601
602            json_obj.insert(
603                "dynamic_type_info".to_string(),
604                if let Some(ref info) = alloc.dynamic_type_info {
605                    serde_json::to_value(info).unwrap_or_else(|_| serde_json::json!({"type": "static", "runtime_type": "unknown"}))
606                } else {
607                    serde_json::json!({"type": "static", "runtime_type": self.infer_type_name(alloc)})
608                },
609            );
610
611            json_obj.insert(
612                "generic_instantiation".to_string(),
613                if let Some(ref info) = alloc.generic_instantiation {
614                    serde_json::to_value(info).unwrap_or_else(
615                        |_| serde_json::json!({"instantiation": "none", "parameters": []}),
616                    )
617                } else {
618                    serde_json::json!({"instantiation": "none", "parameters": []})
619                },
620            );
621
622            json_obj.insert(
623                "type_relationships".to_string(),
624                if let Some(ref info) = alloc.type_relationships {
625                    serde_json::to_value(info).unwrap_or_else(
626                        |_| serde_json::json!({"relationships": [], "inheritance": "none"}),
627                    )
628                } else {
629                    serde_json::json!({"relationships": [], "inheritance": "none"})
630                },
631            );
632
633            json_obj.insert(
634                "type_usage".to_string(),
635                if let Some(ref info) = alloc.type_usage {
636                    serde_json::to_value(info).unwrap_or_else(
637                        |_| serde_json::json!({"usage_count": 1, "usage_pattern": "single"}),
638                    )
639                } else {
640                    serde_json::json!({"usage_count": 1, "usage_pattern": "single"})
641                },
642            );
643
644            entry.push(serde_json::Value::Object(json_obj));
645
646            // Track generic types separately
647            if category == "generic" {
648                let entry = generic_types.entry(type_name.to_string()).or_insert((0, 0));
649                entry.0 += 1; // count
650                entry.1 += alloc.size; // total size
651            }
652        }
653
654        let generic_stats: std::collections::HashMap<String, serde_json::Value> = generic_types
655            .into_iter()
656            .map(|(type_name, (count, total_size))| {
657                (
658                    type_name,
659                    serde_json::json!({
660                        "instantiation_count": count,
661                        "total_size": total_size,
662                        "average_size": if count > 0 { total_size / count } else { 0 }
663                    }),
664                )
665            })
666            .collect();
667
668        let data = serde_json::json!({
669            "categorized_types": categorized_types,
670            "generic_types": generic_stats,
671            "metadata": {
672                "analysis_type": "integrated_complex_types_analysis",
673                "export_version": "2.0",
674                "optimization_level": format!("{:?}", self.config.optimization_level),
675                "timestamp": std::time::SystemTime::now()
676                    .duration_since(std::time::UNIX_EPOCH)
677                    .unwrap_or_default()
678                    .as_secs(),
679                "total_allocations_analyzed": allocations.len(),
680                "pipeline_features": {
681                    "type_categorization": true,
682                    "generic_analysis": true,
683                    "memory_layout_analysis": true
684                }
685            },
686            "summary": {
687                "total_allocations": allocations.len(),
688                "type_categories": categorized_types.len(),
689                "generic_types": generic_stats.len(),
690                "complex_type_ratio": if !allocations.is_empty() {
691                    (categorized_types.get("generic").map(|v| v.len()).unwrap_or(0) as f64 / allocations.len() as f64) * 100.0
692                } else { 0.0 }
693            }
694        });
695
696        Ok(AnalysisData {
697            data,
698            metadata: AnalysisMetadata {
699                analysis_type: "integrated_complex_types_analysis".to_string(),
700                timestamp: std::time::SystemTime::now()
701                    .duration_since(std::time::UNIX_EPOCH)
702                    .unwrap_or_default()
703                    .as_secs(),
704                total_allocations: allocations.len(),
705                optimization_level: format!("{:?}", self.config.optimization_level),
706            },
707        })
708    }
709
710    fn get_config(&self) -> &AnalysisConfig {
711        &self.config
712    }
713}
714
715impl StandardAnalysisEngine {
716    /// Convert our analysis config to the existing OptimizedExportOptions
717    #[allow(dead_code)]
718    fn convert_to_export_options(
719        &self,
720    ) -> crate::export::optimized_json_export::OptimizedExportOptions {
721        use crate::export::optimized_json_export::{
722            OptimizationLevel as ExportOptLevel, OptimizedExportOptions,
723        };
724
725        let export_opt_level = match self.config.optimization_level {
726            OptimizationLevel::Low => ExportOptLevel::Low,
727            OptimizationLevel::Medium => ExportOptLevel::Medium,
728            OptimizationLevel::High => ExportOptLevel::High,
729            OptimizationLevel::Maximum => ExportOptLevel::Maximum,
730        };
731
732        OptimizedExportOptions::with_optimization_level(export_opt_level)
733            .parallel_processing(self.config.parallel_processing)
734            .batch_size(self.config.batch_size)
735    }
736
737    /// Infer type name from allocation when type_name is None
738    /// This eliminates "unknown" type names in full-binary mode
739    fn infer_type_name(&self, alloc: &AllocationInfo) -> String {
740        match alloc.type_name.as_deref() {
741            Some(name) => name.to_string(),
742            None => {
743                // Infer type from allocation size and patterns
744                match alloc.size {
745                    0 => "ZeroSizedType".to_string(),
746                    1 => "u8_or_bool".to_string(),
747                    2 => "u16_or_char".to_string(),
748                    4 => "u32_or_f32_or_i32".to_string(),
749                    8 => "u64_or_f64_or_i64_or_usize".to_string(),
750                    16 => "u128_or_i128_or_complex_struct".to_string(),
751                    24 => "Vec_or_String_header".to_string(),
752                    32 => "HashMap_or_BTreeMap_header".to_string(),
753                    size if size >= 1024 => format!("LargeAllocation_{size}bytes"),
754                    size if size % 8 == 0 => format!("AlignedStruct_{size}bytes"),
755                    size => format!("CustomType_{size}bytes"),
756                }
757            }
758        }
759    }
760
761    /// Infer variable name from allocation when var_name is None
762    /// This eliminates "unknown" variable names in full-binary mode
763    fn infer_var_name(&self, alloc: &AllocationInfo) -> String {
764        match alloc.var_name.as_deref() {
765            Some(name) => name.to_string(),
766            None => {
767                // Generate descriptive variable name based on allocation characteristics
768                let type_hint = match alloc.size {
769                    0 => "zero_sized_var",
770                    1..=8 => "primitive_var",
771                    9..=32 => "small_struct_var",
772                    33..=256 => "medium_struct_var",
773                    257..=1024 => "large_struct_var",
774                    _ => "heap_allocated_var",
775                };
776
777                // Include pointer address for uniqueness
778                format!("{}_{:x}", type_hint, alloc.ptr)
779            }
780        }
781    }
782}
783
784#[cfg(test)]
785mod tests {
786    use super::*;
787    use crate::core::types::AllocationInfo;
788
789    fn create_test_allocations() -> Vec<AllocationInfo> {
790        vec![AllocationInfo {
791            ptr: 0x1000,
792            size: 1024,
793            var_name: Some("buffer".to_string()),
794            type_name: Some("Vec<u8>".to_string()),
795            scope_name: Some("main".to_string()),
796            timestamp_alloc: 1234567890,
797            timestamp_dealloc: None,
798            thread_id: "main".to_string(),
799            borrow_count: 0,
800            stack_trace: None,
801            is_leaked: false,
802            lifetime_ms: Some(100),
803            borrow_info: None,
804            clone_info: None,
805            ownership_history_available: false,
806            smart_pointer_info: None,
807            memory_layout: None,
808            generic_info: None,
809            dynamic_type_info: None,
810            runtime_state: None,
811            stack_allocation: None,
812            temporary_object: None,
813            fragmentation_analysis: None,
814            generic_instantiation: None,
815            type_relationships: None,
816            type_usage: None,
817            function_call_tracking: None,
818            lifecycle_tracking: None,
819            access_tracking: None,
820            drop_chain_analysis: None,
821        }]
822    }
823
824    #[test]
825    fn test_standard_analysis_engine_creation() {
826        let engine = StandardAnalysisEngine::new();
827        assert_eq!(
828            engine.get_config().optimization_level,
829            OptimizationLevel::High
830        );
831    }
832
833    #[test]
834    fn test_memory_analysis() {
835        let engine = StandardAnalysisEngine::new();
836        let allocations = create_test_allocations();
837
838        let result = engine.create_memory_analysis(&allocations);
839        assert!(result.is_ok());
840
841        let analysis_data = result.expect("Failed to get analysis data");
842        assert_eq!(
843            analysis_data.metadata.analysis_type,
844            "integrated_memory_analysis"
845        );
846        assert_eq!(analysis_data.metadata.total_allocations, 1);
847    }
848
849    #[test]
850    fn test_standard_analysis_engine_with_config() {
851        let config = AnalysisConfig {
852            optimization_level: OptimizationLevel::Low,
853            parallel_processing: false,
854            enhanced_ffi_analysis: false,
855            security_analysis: true,
856            batch_size: 500,
857        };
858        let engine = StandardAnalysisEngine::with_config(config);
859
860        assert_eq!(
861            engine.get_config().optimization_level,
862            OptimizationLevel::Low
863        );
864        assert!(!engine.get_config().parallel_processing);
865        assert!(!engine.get_config().enhanced_ffi_analysis);
866        assert!(engine.get_config().security_analysis);
867        assert_eq!(engine.get_config().batch_size, 500);
868    }
869
870    #[test]
871    fn test_standard_analysis_engine_default() {
872        let engine1 = StandardAnalysisEngine::new();
873        let engine2 = StandardAnalysisEngine::default();
874
875        assert_eq!(
876            engine1.get_config().optimization_level,
877            engine2.get_config().optimization_level
878        );
879        assert_eq!(
880            engine1.get_config().parallel_processing,
881            engine2.get_config().parallel_processing
882        );
883        assert_eq!(
884            engine1.get_config().batch_size,
885            engine2.get_config().batch_size
886        );
887    }
888
889    #[test]
890    fn test_analysis_config_default() {
891        let config = AnalysisConfig::default();
892
893        assert_eq!(config.optimization_level, OptimizationLevel::High);
894        assert!(config.parallel_processing);
895        assert!(config.enhanced_ffi_analysis);
896        assert!(!config.security_analysis);
897        assert_eq!(config.batch_size, 1000);
898    }
899
900    #[test]
901    fn test_lifetime_analysis() {
902        let engine = StandardAnalysisEngine::new();
903        let allocations = create_test_allocations();
904
905        let result = engine.create_lifetime_analysis(&allocations);
906        assert!(result.is_ok());
907
908        let analysis_data = result.expect("Lifetime analysis should succeed with valid test data");
909        assert_eq!(
910            analysis_data.metadata.analysis_type,
911            "integrated_lifetime_analysis"
912        );
913        assert_eq!(analysis_data.metadata.total_allocations, 1);
914
915        // Check that the data contains expected fields
916        let data = &analysis_data.data;
917        assert!(data.get("lifecycle_events").is_some());
918        assert!(data.get("scope_analysis").is_some());
919        assert!(data.get("summary").is_some());
920    }
921
922    #[test]
923    fn test_performance_analysis() {
924        let engine = StandardAnalysisEngine::new();
925        let allocations = create_test_allocations();
926
927        let result = engine.create_performance_analysis(&allocations);
928        assert!(result.is_ok());
929
930        let analysis_data = result.unwrap();
931        assert_eq!(
932            analysis_data.metadata.analysis_type,
933            "integrated_performance_analysis"
934        );
935        assert_eq!(analysis_data.metadata.total_allocations, 1);
936
937        // Check that the data contains expected fields
938        let data = &analysis_data.data;
939        assert!(data.get("allocations").is_some());
940        assert!(data.get("thread_analysis").is_some());
941        assert!(data.get("summary").is_some());
942    }
943
944    #[test]
945    fn test_complex_types_analysis() {
946        let engine = StandardAnalysisEngine::new();
947        let allocations = create_test_allocations();
948
949        let result = engine.create_complex_types_analysis(&allocations);
950        assert!(result.is_ok());
951
952        let analysis_data = result.unwrap();
953        assert_eq!(
954            analysis_data.metadata.analysis_type,
955            "integrated_complex_types_analysis"
956        );
957        assert_eq!(analysis_data.metadata.total_allocations, 1);
958
959        // Check that the data contains expected fields
960        let data = &analysis_data.data;
961        assert!(data.get("categorized_types").is_some());
962        assert!(data.get("generic_types").is_some());
963        assert!(data.get("summary").is_some());
964    }
965
966    #[test]
967    fn test_empty_allocations() {
968        let engine = StandardAnalysisEngine::new();
969        let empty_allocations = vec![];
970
971        // Test all analysis methods with empty data
972        let memory_result = engine.create_memory_analysis(&empty_allocations);
973        assert!(memory_result.is_ok());
974        let memory_data = memory_result.expect("Memory analysis should succeed with empty data");
975        assert_eq!(memory_data.metadata.total_allocations, 0);
976
977        let lifetime_result = engine.create_lifetime_analysis(&empty_allocations);
978        assert!(lifetime_result.is_ok());
979        let lifetime_data =
980            lifetime_result.expect("Lifetime analysis should succeed with empty data");
981        assert_eq!(lifetime_data.metadata.total_allocations, 0);
982
983        let performance_result = engine.create_performance_analysis(&empty_allocations);
984        assert!(performance_result.is_ok());
985        let performance_data =
986            performance_result.expect("Performance analysis should succeed with empty data");
987        assert_eq!(performance_data.metadata.total_allocations, 0);
988
989        let complex_types_result = engine.create_complex_types_analysis(&empty_allocations);
990        assert!(complex_types_result.is_ok());
991        let complex_types_data =
992            complex_types_result.expect("Complex types analysis should succeed with empty data");
993        assert_eq!(complex_types_data.metadata.total_allocations, 0);
994    }
995
996    fn create_multiple_test_allocations() -> Vec<AllocationInfo> {
997        vec![
998            AllocationInfo {
999                ptr: 0x1000,
1000                size: 1024,
1001                var_name: Some("buffer".to_string()),
1002                type_name: Some("Vec<u8>".to_string()),
1003                scope_name: Some("main".to_string()),
1004                timestamp_alloc: 1234567890,
1005                timestamp_dealloc: Some(1234567990),
1006                thread_id: "main".to_string(),
1007                borrow_count: 0,
1008                stack_trace: Some(vec!["main".to_string(), "allocate".to_string()]),
1009                is_leaked: false,
1010                lifetime_ms: Some(100),
1011                borrow_info: None,
1012                clone_info: None,
1013                ownership_history_available: false,
1014                smart_pointer_info: None,
1015                memory_layout: None,
1016                generic_info: None,
1017                dynamic_type_info: None,
1018                runtime_state: None,
1019                stack_allocation: None,
1020                temporary_object: None,
1021                fragmentation_analysis: None,
1022                generic_instantiation: None,
1023                type_relationships: None,
1024                type_usage: None,
1025                function_call_tracking: None,
1026                lifecycle_tracking: None,
1027                access_tracking: None,
1028                drop_chain_analysis: None,
1029            },
1030            AllocationInfo {
1031                ptr: 0x2000,
1032                size: 512,
1033                var_name: None,  // Test inference
1034                type_name: None, // Test inference
1035                scope_name: Some("function".to_string()),
1036                timestamp_alloc: 1234567900,
1037                timestamp_dealloc: None,
1038                thread_id: "worker".to_string(),
1039                borrow_count: 2,
1040                stack_trace: None,
1041                is_leaked: true,
1042                lifetime_ms: None,
1043                borrow_info: None,
1044                clone_info: None,
1045                ownership_history_available: false,
1046                smart_pointer_info: None,
1047                memory_layout: None,
1048                generic_info: None,
1049                dynamic_type_info: None,
1050                runtime_state: None,
1051                stack_allocation: None,
1052                temporary_object: None,
1053                fragmentation_analysis: None,
1054                generic_instantiation: None,
1055                type_relationships: None,
1056                type_usage: None,
1057                function_call_tracking: None,
1058                lifecycle_tracking: None,
1059                access_tracking: None,
1060                drop_chain_analysis: None,
1061            },
1062            AllocationInfo {
1063                ptr: 0x3000,
1064                size: 8,
1065                var_name: Some("counter".to_string()),
1066                type_name: Some("HashMap<String, i32>".to_string()),
1067                scope_name: None, // Test global scope
1068                timestamp_alloc: 1234567910,
1069                timestamp_dealloc: Some(1234567950),
1070                thread_id: "main".to_string(),
1071                borrow_count: 1,
1072                stack_trace: Some(vec!["main".to_string()]),
1073                is_leaked: false,
1074                lifetime_ms: Some(40),
1075                borrow_info: None,
1076                clone_info: None,
1077                ownership_history_available: false,
1078                smart_pointer_info: None,
1079                memory_layout: None,
1080                generic_info: None,
1081                dynamic_type_info: None,
1082                runtime_state: None,
1083                stack_allocation: None,
1084                temporary_object: None,
1085                fragmentation_analysis: None,
1086                generic_instantiation: None,
1087                type_relationships: None,
1088                type_usage: None,
1089                function_call_tracking: None,
1090                lifecycle_tracking: None,
1091                access_tracking: None,
1092                drop_chain_analysis: None,
1093            },
1094        ]
1095    }
1096
1097    #[test]
1098    fn test_multiple_allocations_analysis() {
1099        let engine = StandardAnalysisEngine::new();
1100        let allocations = create_multiple_test_allocations();
1101
1102        // Test memory analysis with multiple allocations
1103        let memory_result = engine.create_memory_analysis(&allocations);
1104        assert!(memory_result.is_ok());
1105        let memory_data = memory_result.unwrap();
1106        assert_eq!(memory_data.metadata.total_allocations, 3);
1107
1108        // Check summary calculations
1109        let summary = memory_data.data.get("summary").unwrap();
1110        assert_eq!(
1111            summary.get("total_allocations").unwrap().as_u64().unwrap(),
1112            3
1113        );
1114        assert_eq!(summary.get("total_memory").unwrap().as_u64().unwrap(), 1544); // 1024 + 512 + 8
1115        assert_eq!(summary.get("leaked_count").unwrap().as_u64().unwrap(), 1);
1116        assert_eq!(summary.get("max_size").unwrap().as_u64().unwrap(), 1024);
1117        assert_eq!(summary.get("min_size").unwrap().as_u64().unwrap(), 8);
1118    }
1119
1120    #[test]
1121    fn test_lifetime_analysis_with_multiple_allocations() {
1122        let engine = StandardAnalysisEngine::new();
1123        let allocations = create_multiple_test_allocations();
1124
1125        let result = engine.create_lifetime_analysis(&allocations);
1126        assert!(result.is_ok());
1127        let analysis_data = result.unwrap();
1128
1129        // Check lifecycle events
1130        let events = analysis_data
1131            .data
1132            .get("lifecycle_events")
1133            .unwrap()
1134            .as_array()
1135            .unwrap();
1136        assert_eq!(events.len(), 5); // 3 allocations + 2 deallocations
1137
1138        // Check scope analysis
1139        let scope_analysis = analysis_data
1140            .data
1141            .get("scope_analysis")
1142            .unwrap()
1143            .as_object()
1144            .unwrap();
1145        assert!(scope_analysis.contains_key("main"));
1146        assert!(scope_analysis.contains_key("function"));
1147        assert!(scope_analysis.contains_key("global"));
1148    }
1149
1150    #[test]
1151    fn test_performance_analysis_with_multiple_threads() {
1152        let engine = StandardAnalysisEngine::new();
1153        let allocations = create_multiple_test_allocations();
1154
1155        let result = engine.create_performance_analysis(&allocations);
1156        assert!(result.is_ok());
1157        let analysis_data = result.unwrap();
1158
1159        // Check thread analysis
1160        let thread_analysis = analysis_data
1161            .data
1162            .get("thread_analysis")
1163            .unwrap()
1164            .as_object()
1165            .unwrap();
1166        assert!(thread_analysis.contains_key("main"));
1167        assert!(thread_analysis.contains_key("worker"));
1168
1169        // Check main thread stats
1170        let main_stats = thread_analysis.get("main").unwrap();
1171        assert_eq!(
1172            main_stats
1173                .get("allocation_count")
1174                .unwrap()
1175                .as_u64()
1176                .unwrap(),
1177            2
1178        );
1179        assert_eq!(
1180            main_stats.get("total_size").unwrap().as_u64().unwrap(),
1181            1032
1182        ); // 1024 + 8
1183
1184        // Check worker thread stats
1185        let worker_stats = thread_analysis.get("worker").unwrap();
1186        assert_eq!(
1187            worker_stats
1188                .get("allocation_count")
1189                .unwrap()
1190                .as_u64()
1191                .unwrap(),
1192            1
1193        );
1194        assert_eq!(
1195            worker_stats.get("total_size").unwrap().as_u64().unwrap(),
1196            512
1197        );
1198    }
1199
1200    #[test]
1201    fn test_complex_types_categorization() {
1202        let engine = StandardAnalysisEngine::new();
1203        let allocations = create_multiple_test_allocations();
1204
1205        let result = engine.create_complex_types_analysis(&allocations);
1206        assert!(result.is_ok());
1207        let analysis_data = result.unwrap();
1208
1209        // Check categorized types
1210        let categorized = analysis_data
1211            .data
1212            .get("categorized_types")
1213            .unwrap()
1214            .as_object()
1215            .unwrap();
1216        assert!(categorized.contains_key("generic")); // Vec<u8> and HashMap<String, i32>
1217        assert!(categorized.contains_key("primitive")); // Inferred type for size 512
1218
1219        // Check generic types analysis
1220        let generic_types = analysis_data
1221            .data
1222            .get("generic_types")
1223            .unwrap()
1224            .as_object()
1225            .unwrap();
1226        assert!(generic_types.contains_key("Vec<u8>"));
1227        assert!(generic_types.contains_key("HashMap<String, i32>"));
1228    }
1229
1230    #[test]
1231    fn test_type_name_inference() {
1232        let engine = StandardAnalysisEngine::new();
1233
1234        // Test different size patterns
1235        let test_cases = vec![
1236            (0, "ZeroSizedType"),
1237            (1, "u8_or_bool"),
1238            (2, "u16_or_char"),
1239            (4, "u32_or_f32_or_i32"),
1240            (8, "u64_or_f64_or_i64_or_usize"),
1241            (16, "u128_or_i128_or_complex_struct"),
1242            (24, "Vec_or_String_header"),
1243            (32, "HashMap_or_BTreeMap_header"),
1244            (1024, "LargeAllocation_1024bytes"),
1245            (48, "AlignedStruct_48bytes"), // 48 % 8 == 0
1246            (33, "CustomType_33bytes"),    // 33 % 8 != 0
1247        ];
1248
1249        for (size, expected_prefix) in test_cases {
1250            let alloc = AllocationInfo {
1251                ptr: 0x1000,
1252                size,
1253                var_name: None,
1254                type_name: None, // Force inference
1255                scope_name: None,
1256                timestamp_alloc: 0,
1257                timestamp_dealloc: None,
1258                thread_id: "test".to_string(),
1259                borrow_count: 0,
1260                stack_trace: None,
1261                is_leaked: false,
1262                lifetime_ms: None,
1263                borrow_info: None,
1264                clone_info: None,
1265                ownership_history_available: false,
1266                smart_pointer_info: None,
1267                memory_layout: None,
1268                generic_info: None,
1269                dynamic_type_info: None,
1270                runtime_state: None,
1271                stack_allocation: None,
1272                temporary_object: None,
1273                fragmentation_analysis: None,
1274                generic_instantiation: None,
1275                type_relationships: None,
1276                type_usage: None,
1277                function_call_tracking: None,
1278                lifecycle_tracking: None,
1279                access_tracking: None,
1280                drop_chain_analysis: None,
1281            };
1282
1283            let inferred_type = engine.infer_type_name(&alloc);
1284            assert_eq!(inferred_type, expected_prefix);
1285        }
1286    }
1287
1288    #[test]
1289    fn test_var_name_inference() {
1290        let engine = StandardAnalysisEngine::new();
1291
1292        let test_cases = vec![
1293            (0, "zero_sized_var"),
1294            (4, "primitive_var"),
1295            (16, "small_struct_var"),
1296            (128, "medium_struct_var"),
1297            (512, "large_struct_var"),
1298            (2048, "heap_allocated_var"),
1299        ];
1300
1301        for (size, expected_prefix) in test_cases {
1302            let alloc = AllocationInfo {
1303                ptr: 0x1234,
1304                size,
1305                var_name: None, // Force inference
1306                type_name: Some("TestType".to_string()),
1307                scope_name: None,
1308                timestamp_alloc: 0,
1309                timestamp_dealloc: None,
1310                thread_id: "test".to_string(),
1311                borrow_count: 0,
1312                stack_trace: None,
1313                is_leaked: false,
1314                lifetime_ms: None,
1315                borrow_info: None,
1316                clone_info: None,
1317                ownership_history_available: false,
1318                smart_pointer_info: None,
1319                memory_layout: None,
1320                generic_info: None,
1321                dynamic_type_info: None,
1322                runtime_state: None,
1323                stack_allocation: None,
1324                temporary_object: None,
1325                fragmentation_analysis: None,
1326                generic_instantiation: None,
1327                type_relationships: None,
1328                type_usage: None,
1329                function_call_tracking: None,
1330                lifecycle_tracking: None,
1331                access_tracking: None,
1332                drop_chain_analysis: None,
1333            };
1334
1335            let inferred_var = engine.infer_var_name(&alloc);
1336            assert!(inferred_var.starts_with(expected_prefix));
1337            assert!(inferred_var.contains("1234")); // Should contain pointer address
1338        }
1339    }
1340
1341    #[test]
1342    fn test_optimization_levels() {
1343        // Test all optimization levels
1344        let levels = vec![
1345            OptimizationLevel::Low,
1346            OptimizationLevel::Medium,
1347            OptimizationLevel::High,
1348            OptimizationLevel::Maximum,
1349        ];
1350
1351        for level in levels {
1352            let config = AnalysisConfig {
1353                optimization_level: level.clone(),
1354                parallel_processing: true,
1355                enhanced_ffi_analysis: true,
1356                security_analysis: false,
1357                batch_size: 1000,
1358            };
1359
1360            let engine = StandardAnalysisEngine::with_config(config);
1361            let allocations = create_test_allocations();
1362
1363            // Test that all analysis methods work with different optimization levels
1364            let memory_result = engine.create_memory_analysis(&allocations);
1365            assert!(memory_result.is_ok());
1366            let memory_data = memory_result.unwrap();
1367            assert_eq!(
1368                memory_data.metadata.optimization_level,
1369                format!("{:?}", level)
1370            );
1371
1372            let lifetime_result = engine.create_lifetime_analysis(&allocations);
1373            assert!(lifetime_result.is_ok());
1374
1375            let performance_result = engine.create_performance_analysis(&allocations);
1376            assert!(performance_result.is_ok());
1377
1378            let complex_types_result = engine.create_complex_types_analysis(&allocations);
1379            assert!(complex_types_result.is_ok());
1380        }
1381    }
1382
1383    #[test]
1384    fn test_analysis_error_display() {
1385        let processing_error = AnalysisError::ProcessingError("Test processing error".to_string());
1386        assert_eq!(
1387            processing_error.to_string(),
1388            "Processing error: Test processing error"
1389        );
1390
1391        let serialization_error =
1392            AnalysisError::SerializationError("Test serialization error".to_string());
1393        assert_eq!(
1394            serialization_error.to_string(),
1395            "Serialization error: Test serialization error"
1396        );
1397
1398        let invalid_data_error = AnalysisError::InvalidData("Test invalid data".to_string());
1399        assert_eq!(
1400            invalid_data_error.to_string(),
1401            "Invalid data: Test invalid data"
1402        );
1403    }
1404
1405    #[test]
1406    fn test_analysis_error_debug() {
1407        let error = AnalysisError::ProcessingError("Debug test".to_string());
1408        let debug_str = format!("{:?}", error);
1409        assert!(debug_str.contains("ProcessingError"));
1410        assert!(debug_str.contains("Debug test"));
1411    }
1412
1413    #[test]
1414    fn test_analysis_data_debug_and_clone() {
1415        let metadata = AnalysisMetadata {
1416            analysis_type: "test_analysis".to_string(),
1417            timestamp: 1234567890,
1418            total_allocations: 10,
1419            optimization_level: "High".to_string(),
1420        };
1421
1422        let data = AnalysisData {
1423            data: serde_json::json!({"test": "value"}),
1424            metadata: metadata.clone(),
1425        };
1426
1427        // Test Debug implementation
1428        let debug_str = format!("{:?}", data);
1429        assert!(debug_str.contains("AnalysisData"));
1430        assert!(debug_str.contains("test_analysis"));
1431
1432        // Test Clone implementation
1433        let cloned_data = data.clone();
1434        assert_eq!(
1435            cloned_data.metadata.analysis_type,
1436            data.metadata.analysis_type
1437        );
1438        assert_eq!(cloned_data.metadata.timestamp, data.metadata.timestamp);
1439        assert_eq!(
1440            cloned_data.metadata.total_allocations,
1441            data.metadata.total_allocations
1442        );
1443    }
1444
1445    #[test]
1446    fn test_analysis_metadata_debug_and_clone() {
1447        let metadata = AnalysisMetadata {
1448            analysis_type: "test_metadata".to_string(),
1449            timestamp: 9876543210,
1450            total_allocations: 42,
1451            optimization_level: "Maximum".to_string(),
1452        };
1453
1454        // Test Debug implementation
1455        let debug_str = format!("{:?}", metadata);
1456        assert!(debug_str.contains("AnalysisMetadata"));
1457        assert!(debug_str.contains("test_metadata"));
1458        assert!(debug_str.contains("42"));
1459
1460        // Test Clone implementation
1461        let cloned_metadata = metadata.clone();
1462        assert_eq!(cloned_metadata.analysis_type, metadata.analysis_type);
1463        assert_eq!(cloned_metadata.timestamp, metadata.timestamp);
1464        assert_eq!(
1465            cloned_metadata.total_allocations,
1466            metadata.total_allocations
1467        );
1468        assert_eq!(
1469            cloned_metadata.optimization_level,
1470            metadata.optimization_level
1471        );
1472    }
1473
1474    #[test]
1475    fn test_analysis_config_debug_and_clone() {
1476        let config = AnalysisConfig {
1477            optimization_level: OptimizationLevel::Medium,
1478            parallel_processing: false,
1479            enhanced_ffi_analysis: true,
1480            security_analysis: true,
1481            batch_size: 2000,
1482        };
1483
1484        // Test Debug implementation
1485        let debug_str = format!("{:?}", config);
1486        assert!(debug_str.contains("AnalysisConfig"));
1487        assert!(debug_str.contains("Medium"));
1488        assert!(debug_str.contains("2000"));
1489
1490        // Test Clone implementation
1491        let cloned_config = config.clone();
1492        assert_eq!(cloned_config.optimization_level, config.optimization_level);
1493        assert_eq!(
1494            cloned_config.parallel_processing,
1495            config.parallel_processing
1496        );
1497        assert_eq!(
1498            cloned_config.enhanced_ffi_analysis,
1499            config.enhanced_ffi_analysis
1500        );
1501        assert_eq!(cloned_config.security_analysis, config.security_analysis);
1502        assert_eq!(cloned_config.batch_size, config.batch_size);
1503    }
1504
1505    #[test]
1506    fn test_optimization_level_equality() {
1507        assert_eq!(OptimizationLevel::Low, OptimizationLevel::Low);
1508        assert_eq!(OptimizationLevel::Medium, OptimizationLevel::Medium);
1509        assert_eq!(OptimizationLevel::High, OptimizationLevel::High);
1510        assert_eq!(OptimizationLevel::Maximum, OptimizationLevel::Maximum);
1511
1512        assert_ne!(OptimizationLevel::Low, OptimizationLevel::Medium);
1513        assert_ne!(OptimizationLevel::Medium, OptimizationLevel::High);
1514        assert_ne!(OptimizationLevel::High, OptimizationLevel::Maximum);
1515    }
1516
1517    #[test]
1518    fn test_convert_to_export_options() {
1519        let engine = StandardAnalysisEngine::new();
1520        let export_options = engine.convert_to_export_options();
1521
1522        // This tests the conversion function exists and works
1523        // The actual values depend on the implementation
1524        assert_eq!(export_options.batch_size, 1000);
1525        assert!(export_options.parallel_processing);
1526    }
1527}