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.unwrap();
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.unwrap();
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 = lifetime_result.unwrap();
980        assert_eq!(lifetime_data.metadata.total_allocations, 0);
981
982        let performance_result = engine.create_performance_analysis(&empty_allocations);
983        assert!(performance_result.is_ok());
984        let performance_data = performance_result.unwrap();
985        assert_eq!(performance_data.metadata.total_allocations, 0);
986
987        let complex_types_result = engine.create_complex_types_analysis(&empty_allocations);
988        assert!(complex_types_result.is_ok());
989        let complex_types_data = complex_types_result.unwrap();
990        assert_eq!(complex_types_data.metadata.total_allocations, 0);
991    }
992
993    fn create_multiple_test_allocations() -> Vec<AllocationInfo> {
994        vec![
995            AllocationInfo {
996                ptr: 0x1000,
997                size: 1024,
998                var_name: Some("buffer".to_string()),
999                type_name: Some("Vec<u8>".to_string()),
1000                scope_name: Some("main".to_string()),
1001                timestamp_alloc: 1234567890,
1002                timestamp_dealloc: Some(1234567990),
1003                thread_id: "main".to_string(),
1004                borrow_count: 0,
1005                stack_trace: Some(vec!["main".to_string(), "allocate".to_string()]),
1006                is_leaked: false,
1007                lifetime_ms: Some(100),
1008                borrow_info: None,
1009                clone_info: None,
1010                ownership_history_available: false,
1011                smart_pointer_info: None,
1012                memory_layout: None,
1013                generic_info: None,
1014                dynamic_type_info: None,
1015                runtime_state: None,
1016                stack_allocation: None,
1017                temporary_object: None,
1018                fragmentation_analysis: None,
1019                generic_instantiation: None,
1020                type_relationships: None,
1021                type_usage: None,
1022                function_call_tracking: None,
1023                lifecycle_tracking: None,
1024                access_tracking: None,
1025                drop_chain_analysis: None,
1026            },
1027            AllocationInfo {
1028                ptr: 0x2000,
1029                size: 512,
1030                var_name: None,  // Test inference
1031                type_name: None, // Test inference
1032                scope_name: Some("function".to_string()),
1033                timestamp_alloc: 1234567900,
1034                timestamp_dealloc: None,
1035                thread_id: "worker".to_string(),
1036                borrow_count: 2,
1037                stack_trace: None,
1038                is_leaked: true,
1039                lifetime_ms: None,
1040                borrow_info: None,
1041                clone_info: None,
1042                ownership_history_available: false,
1043                smart_pointer_info: None,
1044                memory_layout: None,
1045                generic_info: None,
1046                dynamic_type_info: None,
1047                runtime_state: None,
1048                stack_allocation: None,
1049                temporary_object: None,
1050                fragmentation_analysis: None,
1051                generic_instantiation: None,
1052                type_relationships: None,
1053                type_usage: None,
1054                function_call_tracking: None,
1055                lifecycle_tracking: None,
1056                access_tracking: None,
1057                drop_chain_analysis: None,
1058            },
1059            AllocationInfo {
1060                ptr: 0x3000,
1061                size: 8,
1062                var_name: Some("counter".to_string()),
1063                type_name: Some("HashMap<String, i32>".to_string()),
1064                scope_name: None, // Test global scope
1065                timestamp_alloc: 1234567910,
1066                timestamp_dealloc: Some(1234567950),
1067                thread_id: "main".to_string(),
1068                borrow_count: 1,
1069                stack_trace: Some(vec!["main".to_string()]),
1070                is_leaked: false,
1071                lifetime_ms: Some(40),
1072                borrow_info: None,
1073                clone_info: None,
1074                ownership_history_available: false,
1075                smart_pointer_info: None,
1076                memory_layout: None,
1077                generic_info: None,
1078                dynamic_type_info: None,
1079                runtime_state: None,
1080                stack_allocation: None,
1081                temporary_object: None,
1082                fragmentation_analysis: None,
1083                generic_instantiation: None,
1084                type_relationships: None,
1085                type_usage: None,
1086                function_call_tracking: None,
1087                lifecycle_tracking: None,
1088                access_tracking: None,
1089                drop_chain_analysis: None,
1090            },
1091        ]
1092    }
1093
1094    #[test]
1095    fn test_multiple_allocations_analysis() {
1096        let engine = StandardAnalysisEngine::new();
1097        let allocations = create_multiple_test_allocations();
1098
1099        // Test memory analysis with multiple allocations
1100        let memory_result = engine.create_memory_analysis(&allocations);
1101        assert!(memory_result.is_ok());
1102        let memory_data = memory_result.unwrap();
1103        assert_eq!(memory_data.metadata.total_allocations, 3);
1104
1105        // Check summary calculations
1106        let summary = memory_data.data.get("summary").unwrap();
1107        assert_eq!(
1108            summary.get("total_allocations").unwrap().as_u64().unwrap(),
1109            3
1110        );
1111        assert_eq!(summary.get("total_memory").unwrap().as_u64().unwrap(), 1544); // 1024 + 512 + 8
1112        assert_eq!(summary.get("leaked_count").unwrap().as_u64().unwrap(), 1);
1113        assert_eq!(summary.get("max_size").unwrap().as_u64().unwrap(), 1024);
1114        assert_eq!(summary.get("min_size").unwrap().as_u64().unwrap(), 8);
1115    }
1116
1117    #[test]
1118    fn test_lifetime_analysis_with_multiple_allocations() {
1119        let engine = StandardAnalysisEngine::new();
1120        let allocations = create_multiple_test_allocations();
1121
1122        let result = engine.create_lifetime_analysis(&allocations);
1123        assert!(result.is_ok());
1124        let analysis_data = result.unwrap();
1125
1126        // Check lifecycle events
1127        let events = analysis_data
1128            .data
1129            .get("lifecycle_events")
1130            .unwrap()
1131            .as_array()
1132            .unwrap();
1133        assert_eq!(events.len(), 5); // 3 allocations + 2 deallocations
1134
1135        // Check scope analysis
1136        let scope_analysis = analysis_data
1137            .data
1138            .get("scope_analysis")
1139            .unwrap()
1140            .as_object()
1141            .unwrap();
1142        assert!(scope_analysis.contains_key("main"));
1143        assert!(scope_analysis.contains_key("function"));
1144        assert!(scope_analysis.contains_key("global"));
1145    }
1146
1147    #[test]
1148    fn test_performance_analysis_with_multiple_threads() {
1149        let engine = StandardAnalysisEngine::new();
1150        let allocations = create_multiple_test_allocations();
1151
1152        let result = engine.create_performance_analysis(&allocations);
1153        assert!(result.is_ok());
1154        let analysis_data = result.unwrap();
1155
1156        // Check thread analysis
1157        let thread_analysis = analysis_data
1158            .data
1159            .get("thread_analysis")
1160            .unwrap()
1161            .as_object()
1162            .unwrap();
1163        assert!(thread_analysis.contains_key("main"));
1164        assert!(thread_analysis.contains_key("worker"));
1165
1166        // Check main thread stats
1167        let main_stats = thread_analysis.get("main").unwrap();
1168        assert_eq!(
1169            main_stats
1170                .get("allocation_count")
1171                .unwrap()
1172                .as_u64()
1173                .unwrap(),
1174            2
1175        );
1176        assert_eq!(
1177            main_stats.get("total_size").unwrap().as_u64().unwrap(),
1178            1032
1179        ); // 1024 + 8
1180
1181        // Check worker thread stats
1182        let worker_stats = thread_analysis.get("worker").unwrap();
1183        assert_eq!(
1184            worker_stats
1185                .get("allocation_count")
1186                .unwrap()
1187                .as_u64()
1188                .unwrap(),
1189            1
1190        );
1191        assert_eq!(
1192            worker_stats.get("total_size").unwrap().as_u64().unwrap(),
1193            512
1194        );
1195    }
1196
1197    #[test]
1198    fn test_complex_types_categorization() {
1199        let engine = StandardAnalysisEngine::new();
1200        let allocations = create_multiple_test_allocations();
1201
1202        let result = engine.create_complex_types_analysis(&allocations);
1203        assert!(result.is_ok());
1204        let analysis_data = result.unwrap();
1205
1206        // Check categorized types
1207        let categorized = analysis_data
1208            .data
1209            .get("categorized_types")
1210            .unwrap()
1211            .as_object()
1212            .unwrap();
1213        assert!(categorized.contains_key("generic")); // Vec<u8> and HashMap<String, i32>
1214        assert!(categorized.contains_key("primitive")); // Inferred type for size 512
1215
1216        // Check generic types analysis
1217        let generic_types = analysis_data
1218            .data
1219            .get("generic_types")
1220            .unwrap()
1221            .as_object()
1222            .unwrap();
1223        assert!(generic_types.contains_key("Vec<u8>"));
1224        assert!(generic_types.contains_key("HashMap<String, i32>"));
1225    }
1226
1227    #[test]
1228    fn test_type_name_inference() {
1229        let engine = StandardAnalysisEngine::new();
1230
1231        // Test different size patterns
1232        let test_cases = vec![
1233            (0, "ZeroSizedType"),
1234            (1, "u8_or_bool"),
1235            (2, "u16_or_char"),
1236            (4, "u32_or_f32_or_i32"),
1237            (8, "u64_or_f64_or_i64_or_usize"),
1238            (16, "u128_or_i128_or_complex_struct"),
1239            (24, "Vec_or_String_header"),
1240            (32, "HashMap_or_BTreeMap_header"),
1241            (1024, "LargeAllocation_1024bytes"),
1242            (48, "AlignedStruct_48bytes"), // 48 % 8 == 0
1243            (33, "CustomType_33bytes"),    // 33 % 8 != 0
1244        ];
1245
1246        for (size, expected_prefix) in test_cases {
1247            let alloc = AllocationInfo {
1248                ptr: 0x1000,
1249                size,
1250                var_name: None,
1251                type_name: None, // Force inference
1252                scope_name: None,
1253                timestamp_alloc: 0,
1254                timestamp_dealloc: None,
1255                thread_id: "test".to_string(),
1256                borrow_count: 0,
1257                stack_trace: None,
1258                is_leaked: false,
1259                lifetime_ms: None,
1260                borrow_info: None,
1261                clone_info: None,
1262                ownership_history_available: false,
1263                smart_pointer_info: None,
1264                memory_layout: None,
1265                generic_info: None,
1266                dynamic_type_info: None,
1267                runtime_state: None,
1268                stack_allocation: None,
1269                temporary_object: None,
1270                fragmentation_analysis: None,
1271                generic_instantiation: None,
1272                type_relationships: None,
1273                type_usage: None,
1274                function_call_tracking: None,
1275                lifecycle_tracking: None,
1276                access_tracking: None,
1277                drop_chain_analysis: None,
1278            };
1279
1280            let inferred_type = engine.infer_type_name(&alloc);
1281            assert_eq!(inferred_type, expected_prefix);
1282        }
1283    }
1284
1285    #[test]
1286    fn test_var_name_inference() {
1287        let engine = StandardAnalysisEngine::new();
1288
1289        let test_cases = vec![
1290            (0, "zero_sized_var"),
1291            (4, "primitive_var"),
1292            (16, "small_struct_var"),
1293            (128, "medium_struct_var"),
1294            (512, "large_struct_var"),
1295            (2048, "heap_allocated_var"),
1296        ];
1297
1298        for (size, expected_prefix) in test_cases {
1299            let alloc = AllocationInfo {
1300                ptr: 0x1234,
1301                size,
1302                var_name: None, // Force inference
1303                type_name: Some("TestType".to_string()),
1304                scope_name: None,
1305                timestamp_alloc: 0,
1306                timestamp_dealloc: None,
1307                thread_id: "test".to_string(),
1308                borrow_count: 0,
1309                stack_trace: None,
1310                is_leaked: false,
1311                lifetime_ms: None,
1312                borrow_info: None,
1313                clone_info: None,
1314                ownership_history_available: false,
1315                smart_pointer_info: None,
1316                memory_layout: None,
1317                generic_info: None,
1318                dynamic_type_info: None,
1319                runtime_state: None,
1320                stack_allocation: None,
1321                temporary_object: None,
1322                fragmentation_analysis: None,
1323                generic_instantiation: None,
1324                type_relationships: None,
1325                type_usage: None,
1326                function_call_tracking: None,
1327                lifecycle_tracking: None,
1328                access_tracking: None,
1329                drop_chain_analysis: None,
1330            };
1331
1332            let inferred_var = engine.infer_var_name(&alloc);
1333            assert!(inferred_var.starts_with(expected_prefix));
1334            assert!(inferred_var.contains("1234")); // Should contain pointer address
1335        }
1336    }
1337
1338    #[test]
1339    fn test_optimization_levels() {
1340        // Test all optimization levels
1341        let levels = vec![
1342            OptimizationLevel::Low,
1343            OptimizationLevel::Medium,
1344            OptimizationLevel::High,
1345            OptimizationLevel::Maximum,
1346        ];
1347
1348        for level in levels {
1349            let config = AnalysisConfig {
1350                optimization_level: level.clone(),
1351                parallel_processing: true,
1352                enhanced_ffi_analysis: true,
1353                security_analysis: false,
1354                batch_size: 1000,
1355            };
1356
1357            let engine = StandardAnalysisEngine::with_config(config);
1358            let allocations = create_test_allocations();
1359
1360            // Test that all analysis methods work with different optimization levels
1361            let memory_result = engine.create_memory_analysis(&allocations);
1362            assert!(memory_result.is_ok());
1363            let memory_data = memory_result.unwrap();
1364            assert_eq!(
1365                memory_data.metadata.optimization_level,
1366                format!("{:?}", level)
1367            );
1368
1369            let lifetime_result = engine.create_lifetime_analysis(&allocations);
1370            assert!(lifetime_result.is_ok());
1371
1372            let performance_result = engine.create_performance_analysis(&allocations);
1373            assert!(performance_result.is_ok());
1374
1375            let complex_types_result = engine.create_complex_types_analysis(&allocations);
1376            assert!(complex_types_result.is_ok());
1377        }
1378    }
1379
1380    #[test]
1381    fn test_analysis_error_display() {
1382        let processing_error = AnalysisError::ProcessingError("Test processing error".to_string());
1383        assert_eq!(
1384            processing_error.to_string(),
1385            "Processing error: Test processing error"
1386        );
1387
1388        let serialization_error =
1389            AnalysisError::SerializationError("Test serialization error".to_string());
1390        assert_eq!(
1391            serialization_error.to_string(),
1392            "Serialization error: Test serialization error"
1393        );
1394
1395        let invalid_data_error = AnalysisError::InvalidData("Test invalid data".to_string());
1396        assert_eq!(
1397            invalid_data_error.to_string(),
1398            "Invalid data: Test invalid data"
1399        );
1400    }
1401
1402    #[test]
1403    fn test_analysis_error_debug() {
1404        let error = AnalysisError::ProcessingError("Debug test".to_string());
1405        let debug_str = format!("{:?}", error);
1406        assert!(debug_str.contains("ProcessingError"));
1407        assert!(debug_str.contains("Debug test"));
1408    }
1409
1410    #[test]
1411    fn test_analysis_data_debug_and_clone() {
1412        let metadata = AnalysisMetadata {
1413            analysis_type: "test_analysis".to_string(),
1414            timestamp: 1234567890,
1415            total_allocations: 10,
1416            optimization_level: "High".to_string(),
1417        };
1418
1419        let data = AnalysisData {
1420            data: serde_json::json!({"test": "value"}),
1421            metadata: metadata.clone(),
1422        };
1423
1424        // Test Debug implementation
1425        let debug_str = format!("{:?}", data);
1426        assert!(debug_str.contains("AnalysisData"));
1427        assert!(debug_str.contains("test_analysis"));
1428
1429        // Test Clone implementation
1430        let cloned_data = data.clone();
1431        assert_eq!(
1432            cloned_data.metadata.analysis_type,
1433            data.metadata.analysis_type
1434        );
1435        assert_eq!(cloned_data.metadata.timestamp, data.metadata.timestamp);
1436        assert_eq!(
1437            cloned_data.metadata.total_allocations,
1438            data.metadata.total_allocations
1439        );
1440    }
1441
1442    #[test]
1443    fn test_analysis_metadata_debug_and_clone() {
1444        let metadata = AnalysisMetadata {
1445            analysis_type: "test_metadata".to_string(),
1446            timestamp: 9876543210,
1447            total_allocations: 42,
1448            optimization_level: "Maximum".to_string(),
1449        };
1450
1451        // Test Debug implementation
1452        let debug_str = format!("{:?}", metadata);
1453        assert!(debug_str.contains("AnalysisMetadata"));
1454        assert!(debug_str.contains("test_metadata"));
1455        assert!(debug_str.contains("42"));
1456
1457        // Test Clone implementation
1458        let cloned_metadata = metadata.clone();
1459        assert_eq!(cloned_metadata.analysis_type, metadata.analysis_type);
1460        assert_eq!(cloned_metadata.timestamp, metadata.timestamp);
1461        assert_eq!(
1462            cloned_metadata.total_allocations,
1463            metadata.total_allocations
1464        );
1465        assert_eq!(
1466            cloned_metadata.optimization_level,
1467            metadata.optimization_level
1468        );
1469    }
1470
1471    #[test]
1472    fn test_analysis_config_debug_and_clone() {
1473        let config = AnalysisConfig {
1474            optimization_level: OptimizationLevel::Medium,
1475            parallel_processing: false,
1476            enhanced_ffi_analysis: true,
1477            security_analysis: true,
1478            batch_size: 2000,
1479        };
1480
1481        // Test Debug implementation
1482        let debug_str = format!("{:?}", config);
1483        assert!(debug_str.contains("AnalysisConfig"));
1484        assert!(debug_str.contains("Medium"));
1485        assert!(debug_str.contains("2000"));
1486
1487        // Test Clone implementation
1488        let cloned_config = config.clone();
1489        assert_eq!(cloned_config.optimization_level, config.optimization_level);
1490        assert_eq!(
1491            cloned_config.parallel_processing,
1492            config.parallel_processing
1493        );
1494        assert_eq!(
1495            cloned_config.enhanced_ffi_analysis,
1496            config.enhanced_ffi_analysis
1497        );
1498        assert_eq!(cloned_config.security_analysis, config.security_analysis);
1499        assert_eq!(cloned_config.batch_size, config.batch_size);
1500    }
1501
1502    #[test]
1503    fn test_optimization_level_equality() {
1504        assert_eq!(OptimizationLevel::Low, OptimizationLevel::Low);
1505        assert_eq!(OptimizationLevel::Medium, OptimizationLevel::Medium);
1506        assert_eq!(OptimizationLevel::High, OptimizationLevel::High);
1507        assert_eq!(OptimizationLevel::Maximum, OptimizationLevel::Maximum);
1508
1509        assert_ne!(OptimizationLevel::Low, OptimizationLevel::Medium);
1510        assert_ne!(OptimizationLevel::Medium, OptimizationLevel::High);
1511        assert_ne!(OptimizationLevel::High, OptimizationLevel::Maximum);
1512    }
1513
1514    #[test]
1515    fn test_convert_to_export_options() {
1516        let engine = StandardAnalysisEngine::new();
1517        let export_options = engine.convert_to_export_options();
1518
1519        // This tests the conversion function exists and works
1520        // The actual values depend on the implementation
1521        assert_eq!(export_options.batch_size, 1000);
1522        assert!(export_options.parallel_processing);
1523    }
1524}