memscope_rs/export/
enhanced_json_exporter.rs

1//! Enhanced JSON Exporter - Generates JSON files according to improve.md specifications
2//!
3//! This module creates the exact JSON format specified in improve.md:
4//! 1. memory_analysis.json - Main memory analysis with extended fields
5//! 2. lifetime.json - Ownership history and lifecycle events  
6//! 3. unsafe_ffi.json - FFI safety analysis and memory passports
7
8use crate::analysis::unsafe_ffi_tracker::MemoryPassport;
9use crate::core::types::{
10    AllocationInfo, BorrowInfo, CloneInfo, MemoryStats,
11    TrackingError::{ExportError, SerializationError},
12    TrackingResult,
13};
14use crate::UnsafeReport;
15use serde::{Deserialize, Serialize};
16use std::collections::HashMap;
17use std::path::Path;
18
19/// Ownership event as specified in improve.md
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct OwnershipEvent {
22    /// Event timestamp in nanoseconds
23    pub timestamp: u64,
24    /// Event type: Allocated, Cloned, Dropped, OwnershipTransferred, Borrowed, MutablyBorrowed
25    pub event_type: String,
26    /// ID pointing to the call stack that triggered this event
27    pub source_stack_id: u32,
28    /// Additional details specific to event type
29    pub details: HashMap<String, serde_json::Value>,
30}
31
32/// Lifetime data for a specific allocation as specified in improve.md
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct LifetimeData {
35    /// Allocation pointer that links to memory_analysis.json
36    pub allocation_ptr: usize,
37    /// Array of ownership events for this allocation
38    pub ownership_history: Vec<OwnershipEvent>,
39}
40
41/// Enhanced allocation info with improve.md extensions
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct EnhancedAllocationInfo {
44    /// Memory address of the allocation
45    pub ptr: usize,
46    /// Size of the allocation in bytes
47    pub size: usize,
48    /// Optional variable name associated with this allocation
49    pub var_name: Option<String>,
50    /// Optional type name of the allocated data
51    pub type_name: Option<String>,
52    /// Optional scope name where the allocation occurred
53    pub scope_name: Option<String>,
54    /// Timestamp when the allocation was made
55    pub timestamp_alloc: u64,
56    /// Optional timestamp when the allocation was deallocated
57    pub timestamp_dealloc: Option<u64>,
58    /// Number of active borrows for this allocation
59    pub borrow_count: usize,
60    /// Optional stack trace at the time of allocation
61    pub stack_trace: Option<Vec<String>>,
62    /// Whether this allocation is considered leaked
63    pub is_leaked: bool,
64    /// Precise lifetime in milliseconds (calculated from creation to destruction)
65    pub lifetime_ms: Option<u64>,
66    /// Enhanced borrowing information as specified in improve.md
67    pub borrow_info: Option<BorrowInfo>,
68    /// Enhanced cloning information as specified in improve.md
69    pub clone_info: Option<CloneInfo>,
70    /// Flag indicating if detailed ownership history is available in lifetime.json
71    pub ownership_history_available: bool,
72}
73
74/// Enhanced JSON exporter that creates improve.md compliant output
75pub struct EnhancedJsonExporter {
76    /// Configuration for export behavior
77    config: ExportConfig,
78}
79
80/// Configuration for enhanced JSON export
81#[derive(Debug, Clone)]
82pub struct ExportConfig {
83    /// Enable pretty printing of JSON
84    pub pretty_print: bool,
85    /// Include stack traces in output
86    pub include_stack_traces: bool,
87    /// Generate separate lifetime.json file
88    pub generate_lifetime_file: bool,
89    /// Generate separate unsafe_ffi.json file
90    pub generate_unsafe_ffi_file: bool,
91    /// Maximum number of ownership events per allocation
92    pub max_ownership_events: usize,
93}
94
95impl Default for ExportConfig {
96    fn default() -> Self {
97        Self {
98            pretty_print: true,
99            include_stack_traces: true,
100            generate_lifetime_file: true,
101            generate_unsafe_ffi_file: true,
102            max_ownership_events: 1000,
103        }
104    }
105}
106
107impl EnhancedJsonExporter {
108    /// Create new enhanced JSON exporter
109    pub fn new(config: ExportConfig) -> Self {
110        Self { config }
111    }
112
113    /// Export memory analysis data to improve.md compliant JSON files
114    pub fn export_enhanced_analysis<P: AsRef<Path>>(
115        &self,
116        output_dir: P,
117        memory_stats: &MemoryStats,
118        unsafe_reports: &[UnsafeReport],
119        memory_passports: &[MemoryPassport],
120    ) -> TrackingResult<()> {
121        let output_dir = output_dir.as_ref();
122
123        tracing::info!(
124            "🚀 Starting enhanced JSON export to: {}",
125            output_dir.display()
126        );
127
128        // Create output directory if it doesn't exist
129        std::fs::create_dir_all(output_dir)
130            .map_err(|e| ExportError(format!("Failed to create output directory: {e}",)))?;
131
132        // 1. Export main memory analysis with extended fields
133        self.export_memory_analysis(output_dir, memory_stats)?;
134
135        // 2. Export lifetime and ownership history data
136        if self.config.generate_lifetime_file {
137            self.export_lifetime_data(output_dir, memory_stats)?;
138        }
139
140        // 3. Export unsafe FFI analysis
141        if self.config.generate_unsafe_ffi_file {
142            self.export_unsafe_ffi_analysis(output_dir, unsafe_reports, memory_passports)?;
143        }
144
145        tracing::info!("✅ Enhanced JSON export completed successfully");
146        Ok(())
147    }
148
149    /// Export main memory analysis with improve.md extended fields
150    fn export_memory_analysis<P: AsRef<Path>>(
151        &self,
152        output_dir: P,
153        memory_stats: &MemoryStats,
154    ) -> TrackingResult<()> {
155        let output_path = output_dir.as_ref().join("memory_analysis.json");
156
157        tracing::info!("📊 Exporting memory analysis to: {}", output_path.display());
158
159        // Convert AllocationInfo to EnhancedAllocationInfo with improve.md fields
160        let enhanced_allocations: Vec<EnhancedAllocationInfo> = memory_stats
161            .allocations
162            .iter()
163            .map(|alloc| self.convert_to_enhanced_allocation(alloc))
164            .collect();
165
166        // Create the main analysis structure
167        let analysis_data = serde_json::json!({
168            "metadata": {
169                "export_version": "2.0",
170                "export_timestamp": std::time::SystemTime::now()
171                    .duration_since(std::time::UNIX_EPOCH)
172                    .unwrap_or_default()
173                    .as_secs(),
174                "specification": "improve.md compliant",
175                "total_allocations": enhanced_allocations.len(),
176                "extended_fields_included": true
177            },
178            "summary": {
179                "total_allocations": memory_stats.total_allocations,
180                "total_allocated": memory_stats.total_allocated,
181                "active_allocations": memory_stats.active_allocations,
182                "active_memory": memory_stats.active_memory,
183                "peak_allocations": memory_stats.peak_allocations,
184                "peak_memory": memory_stats.peak_memory,
185                "leaked_allocations": memory_stats.leaked_allocations,
186                "leaked_memory": memory_stats.leaked_memory
187            },
188            "allocations": enhanced_allocations
189        });
190
191        // Write to file
192        self.write_json_file(&output_path, &analysis_data)?;
193
194        tracing::info!(
195            "✅ Memory analysis exported: {} allocations",
196            enhanced_allocations.len()
197        );
198        Ok(())
199    }
200
201    /// Export lifetime and ownership history data as specified in improve.md
202    fn export_lifetime_data<P: AsRef<Path>>(
203        &self,
204        output_dir: P,
205        memory_stats: &MemoryStats,
206    ) -> TrackingResult<()> {
207        let output_path = output_dir.as_ref().join("lifetime.json");
208
209        tracing::info!("🔄 Exporting lifetime data to: {}", output_path.display());
210
211        // Generate lifetime data for allocations that have ownership history
212        let lifetime_data: Vec<LifetimeData> = memory_stats
213            .allocations
214            .iter()
215            .filter(|alloc| alloc.ownership_history_available)
216            .map(|alloc| self.generate_lifetime_data(alloc))
217            .collect();
218
219        let lifetime_export = serde_json::json!({
220            "metadata": {
221                "export_version": "2.0",
222                "export_timestamp": std::time::SystemTime::now()
223                    .duration_since(std::time::UNIX_EPOCH)
224                    .unwrap_or_default()
225                    .as_secs(),
226                "specification": "improve.md lifetime tracking",
227                "total_tracked_allocations": lifetime_data.len()
228            },
229            "ownership_histories": lifetime_data
230        });
231
232        self.write_json_file(&output_path, &lifetime_export)?;
233
234        tracing::info!(
235            "✅ Lifetime data exported: {} tracked allocations",
236            lifetime_data.len()
237        );
238        Ok(())
239    }
240
241    /// Export unsafe FFI analysis as specified in improve.md
242    fn export_unsafe_ffi_analysis<P: AsRef<Path>>(
243        &self,
244        output_dir: P,
245        unsafe_reports: &[UnsafeReport],
246        memory_passports: &[MemoryPassport],
247    ) -> TrackingResult<()> {
248        let output_path = output_dir.as_ref().join("unsafe_ffi.json");
249
250        tracing::info!(
251            "🛡️ Exporting unsafe FFI analysis to: {}",
252            output_path.display()
253        );
254
255        let unsafe_ffi_export = serde_json::json!({
256            "metadata": {
257                "export_version": "2.0",
258                "export_timestamp": std::time::SystemTime::now()
259                    .duration_since(std::time::UNIX_EPOCH)
260                    .unwrap_or_default()
261                    .as_secs(),
262                "specification": "improve.md unsafe FFI tracking",
263                "total_unsafe_reports": unsafe_reports.len(),
264                "total_memory_passports": memory_passports.len()
265            },
266            "unsafe_reports": unsafe_reports,
267            "memory_passports": memory_passports
268        });
269
270        self.write_json_file(&output_path, &unsafe_ffi_export)?;
271
272        tracing::info!(
273            "✅ Unsafe FFI analysis exported: {} reports, {} passports",
274            unsafe_reports.len(),
275            memory_passports.len()
276        );
277        Ok(())
278    }
279
280    /// Convert AllocationInfo to EnhancedAllocationInfo with improve.md fields
281    fn convert_to_enhanced_allocation(&self, alloc: &AllocationInfo) -> EnhancedAllocationInfo {
282        EnhancedAllocationInfo {
283            ptr: alloc.ptr,
284            size: alloc.size,
285            var_name: alloc.var_name.clone(),
286            type_name: alloc.type_name.clone(),
287            scope_name: alloc.scope_name.clone(),
288            timestamp_alloc: alloc.timestamp_alloc,
289            timestamp_dealloc: alloc.timestamp_dealloc,
290            borrow_count: alloc.borrow_count,
291            stack_trace: if self.config.include_stack_traces {
292                alloc.stack_trace.clone()
293            } else {
294                None
295            },
296            is_leaked: alloc.is_leaked,
297            lifetime_ms: alloc.lifetime_ms,
298            // These are the key improve.md extensions:
299            borrow_info: alloc.borrow_info.clone(),
300            clone_info: alloc.clone_info.clone(),
301            ownership_history_available: alloc.ownership_history_available,
302        }
303    }
304
305    /// Generate lifetime data for an allocation as specified in improve.md
306    fn generate_lifetime_data(&self, alloc: &AllocationInfo) -> LifetimeData {
307        let mut ownership_history = Vec::new();
308
309        // Generate Allocated event
310        ownership_history.push(OwnershipEvent {
311            timestamp: alloc.timestamp_alloc,
312            event_type: "Allocated".to_string(),
313            source_stack_id: 1, // Would be actual stack ID in real implementation
314            details: HashMap::new(),
315        });
316
317        // Generate Clone events if applicable
318        if let Some(clone_info) = &alloc.clone_info {
319            if clone_info.is_clone {
320                let mut clone_details = HashMap::new();
321                if let Some(original_ptr) = clone_info.original_ptr {
322                    clone_details.insert(
323                        "clone_source_ptr".to_string(),
324                        serde_json::Value::Number(serde_json::Number::from(original_ptr)),
325                    );
326                }
327
328                ownership_history.push(OwnershipEvent {
329                    timestamp: alloc.timestamp_alloc + 1000, // Simulate clone timestamp
330                    event_type: "Cloned".to_string(),
331                    source_stack_id: 2,
332                    details: clone_details,
333                });
334            }
335        }
336
337        // Generate Borrow events if applicable
338        if let Some(borrow_info) = &alloc.borrow_info {
339            for i in 0..borrow_info.immutable_borrows.min(5) {
340                // Limit to 5 for demo
341                let mut borrow_details = HashMap::new();
342                borrow_details.insert(
343                    "borrower_scope".to_string(),
344                    serde_json::Value::String(format!("scope_{i}")),
345                );
346
347                ownership_history.push(OwnershipEvent {
348                    timestamp: alloc.timestamp_alloc + 2000 + (i as u64 * 1000),
349                    event_type: "Borrowed".to_string(),
350                    source_stack_id: 3 + i as u32,
351                    details: borrow_details,
352                });
353            }
354        }
355
356        // Generate Dropped event if deallocated
357        if let Some(dealloc_timestamp) = alloc.timestamp_dealloc {
358            ownership_history.push(OwnershipEvent {
359                timestamp: dealloc_timestamp,
360                event_type: "Dropped".to_string(),
361                source_stack_id: 99,
362                details: HashMap::new(),
363            });
364        }
365
366        // Limit ownership events to configured maximum
367        ownership_history.truncate(self.config.max_ownership_events);
368
369        LifetimeData {
370            allocation_ptr: alloc.ptr,
371            ownership_history,
372        }
373    }
374
375    /// Write JSON data to file with proper formatting
376    fn write_json_file<P: AsRef<Path>>(
377        &self,
378        path: P,
379        data: &serde_json::Value,
380    ) -> TrackingResult<()> {
381        let json_string = if self.config.pretty_print {
382            serde_json::to_string_pretty(data)
383        } else {
384            serde_json::to_string(data)
385        }
386        .map_err(|e| SerializationError(format!("Failed to serialize JSON: {e}",)))?;
387
388        std::fs::write(&path, json_string).map_err(|e| {
389            ExportError(format!(
390                "Failed to write file {}: {}",
391                path.as_ref().display(),
392                e
393            ))
394        })?;
395
396        Ok(())
397    }
398}
399
400impl Default for EnhancedJsonExporter {
401    fn default() -> Self {
402        Self::new(ExportConfig::default())
403    }
404}
405
406/// Convenience function to export enhanced JSON with default settings
407pub fn export_enhanced_json<P: AsRef<Path>>(
408    output_dir: P,
409    memory_stats: &MemoryStats,
410    unsafe_reports: &[UnsafeReport],
411    memory_passports: &[MemoryPassport],
412) -> TrackingResult<()> {
413    let exporter = EnhancedJsonExporter::default();
414    exporter.export_enhanced_analysis(output_dir, memory_stats, unsafe_reports, memory_passports)
415}
416
417#[cfg(test)]
418mod tests {
419    use super::*;
420    use crate::core::types::{AllocationInfo, BorrowInfo, CloneInfo};
421
422    #[test]
423    fn test_enhanced_allocation_conversion() {
424        let exporter = EnhancedJsonExporter::default();
425
426        let mut alloc = AllocationInfo::new(0x1000, 64);
427        alloc.borrow_info = Some(BorrowInfo {
428            immutable_borrows: 5,
429            mutable_borrows: 2,
430            max_concurrent_borrows: 3,
431            last_borrow_timestamp: Some(1234567890),
432        });
433        alloc.clone_info = Some(CloneInfo {
434            clone_count: 2,
435            is_clone: true,
436            original_ptr: Some(0x2000),
437        });
438        alloc.ownership_history_available = true;
439
440        let enhanced = exporter.convert_to_enhanced_allocation(&alloc);
441
442        assert_eq!(enhanced.ptr, 0x1000);
443        assert_eq!(enhanced.size, 64);
444        assert!(enhanced.borrow_info.is_some());
445        assert!(enhanced.clone_info.is_some());
446        assert!(enhanced.ownership_history_available);
447    }
448
449    #[test]
450    fn test_lifetime_data_generation() {
451        let exporter = EnhancedJsonExporter::default();
452
453        let mut alloc = AllocationInfo::new(0x1000, 64);
454        alloc.borrow_info = Some(BorrowInfo {
455            immutable_borrows: 2,
456            mutable_borrows: 1,
457            max_concurrent_borrows: 2,
458            last_borrow_timestamp: Some(1234567890),
459        });
460        alloc.ownership_history_available = true;
461
462        let lifetime_data = exporter.generate_lifetime_data(&alloc);
463
464        assert_eq!(lifetime_data.allocation_ptr, 0x1000);
465        assert!(!lifetime_data.ownership_history.is_empty());
466
467        // Should have at least Allocated event
468        let allocated_event = lifetime_data
469            .ownership_history
470            .iter()
471            .find(|e| e.event_type == "Allocated");
472        assert!(allocated_event.is_some());
473    }
474
475    fn create_test_allocation(ptr: usize, size: usize) -> AllocationInfo {
476        let mut alloc = AllocationInfo::new(ptr, size);
477        alloc.var_name = Some("test_var".to_string());
478        alloc.type_name = Some("TestType".to_string());
479        alloc.scope_name = Some("test_scope".to_string());
480        alloc.timestamp_alloc = 1234567890;
481        alloc.timestamp_dealloc = None;
482        alloc.borrow_count = 0;
483        alloc.is_leaked = false;
484        alloc.lifetime_ms = Some(1000);
485        alloc
486    }
487
488    fn create_test_memory_stats() -> MemoryStats {
489        let allocations = vec![
490            create_test_allocation(0x1000, 64),
491            create_test_allocation(0x2000, 128),
492        ];
493
494        MemoryStats {
495            total_allocations: 2,
496            total_allocated: 192,
497            active_allocations: 2,
498            active_memory: 192,
499            peak_allocations: 2,
500            peak_memory: 192,
501            total_deallocations: 0,
502            total_deallocated: 0,
503            leaked_allocations: 0,
504            leaked_memory: 0,
505            fragmentation_analysis: crate::core::types::FragmentationAnalysis::default(),
506            lifecycle_stats: crate::core::types::ScopeLifecycleMetrics::default(),
507            allocations,
508            system_library_stats: crate::core::types::SystemLibraryStats::default(),
509            concurrency_analysis: crate::core::types::ConcurrencyAnalysis::default(),
510        }
511    }
512
513    #[test]
514    fn test_export_config_default() {
515        let config = ExportConfig::default();
516
517        assert!(config.pretty_print);
518        assert!(config.include_stack_traces);
519        assert!(config.generate_lifetime_file);
520        assert!(config.generate_unsafe_ffi_file);
521        assert_eq!(config.max_ownership_events, 1000);
522    }
523
524    #[test]
525    fn test_export_config_debug_clone() {
526        let config = ExportConfig {
527            pretty_print: false,
528            include_stack_traces: false,
529            generate_lifetime_file: false,
530            generate_unsafe_ffi_file: false,
531            max_ownership_events: 500,
532        };
533
534        // Test Debug trait
535        let debug_str = format!("{:?}", config);
536        assert!(debug_str.contains("ExportConfig"));
537        assert!(debug_str.contains("pretty_print"));
538        assert!(debug_str.contains("false"));
539
540        // Test Clone trait
541        let cloned_config = config.clone();
542        assert_eq!(cloned_config.pretty_print, config.pretty_print);
543        assert_eq!(
544            cloned_config.include_stack_traces,
545            config.include_stack_traces
546        );
547        assert_eq!(
548            cloned_config.generate_lifetime_file,
549            config.generate_lifetime_file
550        );
551        assert_eq!(
552            cloned_config.generate_unsafe_ffi_file,
553            config.generate_unsafe_ffi_file
554        );
555        assert_eq!(
556            cloned_config.max_ownership_events,
557            config.max_ownership_events
558        );
559    }
560
561    #[test]
562    fn test_enhanced_json_exporter_new() {
563        let config = ExportConfig {
564            pretty_print: false,
565            include_stack_traces: true,
566            generate_lifetime_file: false,
567            generate_unsafe_ffi_file: true,
568            max_ownership_events: 2000,
569        };
570
571        let exporter = EnhancedJsonExporter::new(config.clone());
572        assert_eq!(exporter.config.pretty_print, config.pretty_print);
573        assert_eq!(
574            exporter.config.include_stack_traces,
575            config.include_stack_traces
576        );
577        assert_eq!(
578            exporter.config.generate_lifetime_file,
579            config.generate_lifetime_file
580        );
581        assert_eq!(
582            exporter.config.generate_unsafe_ffi_file,
583            config.generate_unsafe_ffi_file
584        );
585        assert_eq!(
586            exporter.config.max_ownership_events,
587            config.max_ownership_events
588        );
589    }
590
591    #[test]
592    fn test_enhanced_json_exporter_default() {
593        let exporter1 = EnhancedJsonExporter::default();
594        let exporter2 = EnhancedJsonExporter::new(ExportConfig::default());
595
596        assert_eq!(exporter1.config.pretty_print, exporter2.config.pretty_print);
597        assert_eq!(
598            exporter1.config.include_stack_traces,
599            exporter2.config.include_stack_traces
600        );
601        assert_eq!(
602            exporter1.config.generate_lifetime_file,
603            exporter2.config.generate_lifetime_file
604        );
605        assert_eq!(
606            exporter1.config.generate_unsafe_ffi_file,
607            exporter2.config.generate_unsafe_ffi_file
608        );
609        assert_eq!(
610            exporter1.config.max_ownership_events,
611            exporter2.config.max_ownership_events
612        );
613    }
614
615    #[test]
616    fn test_convert_to_enhanced_allocation_with_stack_traces() {
617        let config = ExportConfig {
618            include_stack_traces: true,
619            ..Default::default()
620        };
621        let exporter = EnhancedJsonExporter::new(config);
622
623        let mut alloc = create_test_allocation(0x1000, 64);
624        alloc.stack_trace = Some(vec!["main".to_string(), "allocate".to_string()]);
625        alloc.borrow_info = Some(BorrowInfo {
626            immutable_borrows: 3,
627            mutable_borrows: 1,
628            max_concurrent_borrows: 2,
629            last_borrow_timestamp: Some(1234567890),
630        });
631
632        let enhanced = exporter.convert_to_enhanced_allocation(&alloc);
633
634        assert_eq!(enhanced.ptr, 0x1000);
635        assert_eq!(enhanced.size, 64);
636        assert!(enhanced.stack_trace.is_some());
637        assert_eq!(enhanced.stack_trace.as_ref().unwrap().len(), 2);
638        assert!(enhanced.borrow_info.is_some());
639        assert_eq!(enhanced.borrow_info.as_ref().unwrap().immutable_borrows, 3);
640    }
641
642    #[test]
643    fn test_convert_to_enhanced_allocation_without_stack_traces() {
644        let config = ExportConfig {
645            include_stack_traces: false,
646            ..Default::default()
647        };
648        let exporter = EnhancedJsonExporter::new(config);
649
650        let mut alloc = create_test_allocation(0x1000, 64);
651        alloc.stack_trace = Some(vec!["main".to_string(), "allocate".to_string()]);
652
653        let enhanced = exporter.convert_to_enhanced_allocation(&alloc);
654
655        assert_eq!(enhanced.ptr, 0x1000);
656        assert_eq!(enhanced.size, 64);
657        assert!(enhanced.stack_trace.is_none()); // Should be None due to config
658    }
659
660    #[test]
661    fn test_generate_lifetime_data_with_clone_info() {
662        let exporter = EnhancedJsonExporter::default();
663
664        let mut alloc = create_test_allocation(0x1000, 64);
665        alloc.clone_info = Some(CloneInfo {
666            clone_count: 2,
667            is_clone: true,
668            original_ptr: Some(0x2000),
669        });
670        alloc.ownership_history_available = true;
671
672        let lifetime_data = exporter.generate_lifetime_data(&alloc);
673
674        assert_eq!(lifetime_data.allocation_ptr, 0x1000);
675        assert!(!lifetime_data.ownership_history.is_empty());
676
677        // Should have Allocated and Cloned events
678        let allocated_event = lifetime_data
679            .ownership_history
680            .iter()
681            .find(|e| e.event_type == "Allocated");
682        assert!(allocated_event.is_some());
683
684        let cloned_event = lifetime_data
685            .ownership_history
686            .iter()
687            .find(|e| e.event_type == "Cloned");
688        assert!(cloned_event.is_some());
689
690        // Check clone details
691        let clone_event = cloned_event.unwrap();
692        assert!(clone_event.details.contains_key("clone_source_ptr"));
693    }
694
695    #[test]
696    fn test_generate_lifetime_data_with_borrow_events() {
697        let exporter = EnhancedJsonExporter::default();
698
699        let mut alloc = create_test_allocation(0x1000, 64);
700        alloc.borrow_info = Some(BorrowInfo {
701            immutable_borrows: 3,
702            mutable_borrows: 1,
703            max_concurrent_borrows: 2,
704            last_borrow_timestamp: Some(1234567890),
705        });
706        alloc.ownership_history_available = true;
707
708        let lifetime_data = exporter.generate_lifetime_data(&alloc);
709
710        // Should have Allocated and Borrowed events
711        let borrowed_events: Vec<_> = lifetime_data
712            .ownership_history
713            .iter()
714            .filter(|e| e.event_type == "Borrowed")
715            .collect();
716        assert_eq!(borrowed_events.len(), 3); // Should match immutable_borrows
717
718        // Check borrow details
719        for borrow_event in borrowed_events {
720            assert!(borrow_event.details.contains_key("borrower_scope"));
721        }
722    }
723
724    #[test]
725    fn test_generate_lifetime_data_with_deallocation() {
726        let exporter = EnhancedJsonExporter::default();
727
728        let mut alloc = create_test_allocation(0x1000, 64);
729        alloc.timestamp_dealloc = Some(1234567890 + 5000);
730        alloc.ownership_history_available = true;
731
732        let lifetime_data = exporter.generate_lifetime_data(&alloc);
733
734        // Should have Allocated and Dropped events
735        let dropped_event = lifetime_data
736            .ownership_history
737            .iter()
738            .find(|e| e.event_type == "Dropped");
739        assert!(dropped_event.is_some());
740
741        let drop_event = dropped_event.unwrap();
742        assert_eq!(drop_event.timestamp, 1234567890 + 5000);
743        assert_eq!(drop_event.source_stack_id, 99);
744    }
745
746    #[test]
747    fn test_generate_lifetime_data_with_max_events_limit() {
748        let config = ExportConfig {
749            max_ownership_events: 2,
750            ..Default::default()
751        };
752        let exporter = EnhancedJsonExporter::new(config);
753
754        let mut alloc = create_test_allocation(0x1000, 64);
755        alloc.borrow_info = Some(BorrowInfo {
756            immutable_borrows: 10, // More than max_ownership_events
757            mutable_borrows: 1,
758            max_concurrent_borrows: 5,
759            last_borrow_timestamp: Some(1234567890),
760        });
761        alloc.timestamp_dealloc = Some(1234567890 + 5000);
762        alloc.ownership_history_available = true;
763
764        let lifetime_data = exporter.generate_lifetime_data(&alloc);
765
766        // Should be limited to max_ownership_events
767        assert!(lifetime_data.ownership_history.len() <= 2);
768    }
769
770    #[test]
771    fn test_ownership_event_debug_clone_serialize() {
772        let mut details = HashMap::new();
773        details.insert(
774            "test_key".to_string(),
775            serde_json::Value::String("test_value".to_string()),
776        );
777
778        let event = OwnershipEvent {
779            timestamp: 1234567890,
780            event_type: "TestEvent".to_string(),
781            source_stack_id: 42,
782            details,
783        };
784
785        // Test Debug trait
786        let debug_str = format!("{:?}", event);
787        assert!(debug_str.contains("OwnershipEvent"));
788        assert!(debug_str.contains("TestEvent"));
789        assert!(debug_str.contains("42"));
790
791        // Test Clone trait
792        let cloned_event = event.clone();
793        assert_eq!(cloned_event.timestamp, event.timestamp);
794        assert_eq!(cloned_event.event_type, event.event_type);
795        assert_eq!(cloned_event.source_stack_id, event.source_stack_id);
796        assert_eq!(cloned_event.details.len(), event.details.len());
797
798        // Test Serialize trait
799        let serialized = serde_json::to_string(&event);
800        assert!(serialized.is_ok());
801        let json_str = serialized.unwrap();
802        assert!(json_str.contains("TestEvent"));
803        assert!(json_str.contains("1234567890"));
804
805        // Test Deserialize trait
806        let deserialized: Result<OwnershipEvent, _> = serde_json::from_str(&json_str);
807        assert!(deserialized.is_ok());
808        let deserialized_event = deserialized.unwrap();
809        assert_eq!(deserialized_event.timestamp, event.timestamp);
810        assert_eq!(deserialized_event.event_type, event.event_type);
811    }
812
813    #[test]
814    fn test_lifetime_data_debug_clone_serialize() {
815        let ownership_history = vec![
816            OwnershipEvent {
817                timestamp: 1234567890,
818                event_type: "Allocated".to_string(),
819                source_stack_id: 1,
820                details: HashMap::new(),
821            },
822            OwnershipEvent {
823                timestamp: 1234567900,
824                event_type: "Dropped".to_string(),
825                source_stack_id: 2,
826                details: HashMap::new(),
827            },
828        ];
829
830        let lifetime_data = LifetimeData {
831            allocation_ptr: 0x1000,
832            ownership_history,
833        };
834
835        // Test Debug trait
836        let debug_str = format!("{:?}", lifetime_data);
837        assert!(debug_str.contains("LifetimeData"));
838        assert!(debug_str.contains("4096")); // 0x1000 in decimal
839        assert!(debug_str.contains("Allocated"));
840
841        // Test Clone trait
842        let cloned_data = lifetime_data.clone();
843        assert_eq!(cloned_data.allocation_ptr, lifetime_data.allocation_ptr);
844        assert_eq!(
845            cloned_data.ownership_history.len(),
846            lifetime_data.ownership_history.len()
847        );
848
849        // Test Serialize trait
850        let serialized = serde_json::to_string(&lifetime_data);
851        assert!(serialized.is_ok());
852        let json_str = serialized.unwrap();
853        assert!(json_str.contains("allocation_ptr"));
854        assert!(json_str.contains("ownership_history"));
855
856        // Test Deserialize trait
857        let deserialized: Result<LifetimeData, _> = serde_json::from_str(&json_str);
858        assert!(deserialized.is_ok());
859        let deserialized_data = deserialized.unwrap();
860        assert_eq!(
861            deserialized_data.allocation_ptr,
862            lifetime_data.allocation_ptr
863        );
864        assert_eq!(
865            deserialized_data.ownership_history.len(),
866            lifetime_data.ownership_history.len()
867        );
868    }
869
870    #[test]
871    fn test_enhanced_allocation_info_debug_clone_serialize() {
872        let enhanced_alloc = EnhancedAllocationInfo {
873            ptr: 0x1000,
874            size: 64,
875            var_name: Some("test_var".to_string()),
876            type_name: Some("TestType".to_string()),
877            scope_name: Some("test_scope".to_string()),
878            timestamp_alloc: 1234567890,
879            timestamp_dealloc: Some(1234567900),
880            borrow_count: 2,
881            stack_trace: Some(vec!["main".to_string()]),
882            is_leaked: false,
883            lifetime_ms: Some(1000),
884            borrow_info: Some(BorrowInfo {
885                immutable_borrows: 1,
886                mutable_borrows: 0,
887                max_concurrent_borrows: 1,
888                last_borrow_timestamp: Some(1234567890),
889            }),
890            clone_info: None,
891            ownership_history_available: true,
892        };
893
894        // Test Debug trait
895        let debug_str = format!("{:?}", enhanced_alloc);
896        assert!(debug_str.contains("EnhancedAllocationInfo"));
897        assert!(debug_str.contains("test_var"));
898        assert!(debug_str.contains("TestType"));
899
900        // Test Clone trait
901        let cloned_alloc = enhanced_alloc.clone();
902        assert_eq!(cloned_alloc.ptr, enhanced_alloc.ptr);
903        assert_eq!(cloned_alloc.size, enhanced_alloc.size);
904        assert_eq!(cloned_alloc.var_name, enhanced_alloc.var_name);
905        assert_eq!(
906            cloned_alloc.ownership_history_available,
907            enhanced_alloc.ownership_history_available
908        );
909
910        // Test Serialize trait
911        let serialized = serde_json::to_string(&enhanced_alloc);
912        assert!(serialized.is_ok());
913        let json_str = serialized.unwrap();
914        assert!(json_str.contains("test_var"));
915        assert!(json_str.contains("TestType"));
916        assert!(json_str.contains("ownership_history_available"));
917
918        // Test Deserialize trait
919        let deserialized: Result<EnhancedAllocationInfo, _> = serde_json::from_str(&json_str);
920        assert!(deserialized.is_ok());
921        let deserialized_alloc = deserialized.unwrap();
922        assert_eq!(deserialized_alloc.ptr, enhanced_alloc.ptr);
923        assert_eq!(deserialized_alloc.var_name, enhanced_alloc.var_name);
924        assert_eq!(
925            deserialized_alloc.ownership_history_available,
926            enhanced_alloc.ownership_history_available
927        );
928    }
929
930    #[test]
931    fn test_export_memory_analysis() -> TrackingResult<()> {
932        use tempfile::TempDir;
933
934        let temp_dir = TempDir::new().map_err(|e| ExportError(e.to_string()))?;
935        let exporter = EnhancedJsonExporter::default();
936        let memory_stats = create_test_memory_stats();
937
938        exporter.export_memory_analysis(&temp_dir, &memory_stats)?;
939
940        let output_path = temp_dir.path().join("memory_analysis.json");
941        assert!(output_path.exists());
942
943        // Verify file content
944        let content =
945            std::fs::read_to_string(&output_path).map_err(|e| ExportError(e.to_string()))?;
946        let json_data: serde_json::Value =
947            serde_json::from_str(&content).map_err(|e| SerializationError(e.to_string()))?;
948
949        assert!(json_data.get("metadata").is_some());
950        assert!(json_data.get("summary").is_some());
951        assert!(json_data.get("allocations").is_some());
952
953        let metadata = &json_data["metadata"];
954        assert_eq!(metadata["export_version"].as_str().unwrap(), "2.0");
955        assert_eq!(
956            metadata["specification"].as_str().unwrap(),
957            "improve.md compliant"
958        );
959        assert_eq!(metadata["total_allocations"].as_u64().unwrap(), 2);
960
961        Ok(())
962    }
963
964    #[test]
965    fn test_export_lifetime_data() -> TrackingResult<()> {
966        use tempfile::TempDir;
967
968        let temp_dir = TempDir::new().map_err(|e| ExportError(e.to_string()))?;
969        let exporter = EnhancedJsonExporter::default();
970
971        let mut memory_stats = create_test_memory_stats();
972        // Mark some allocations as having ownership history
973        memory_stats.allocations[0].ownership_history_available = true;
974        memory_stats.allocations[1].ownership_history_available = true;
975
976        exporter.export_lifetime_data(&temp_dir, &memory_stats)?;
977
978        let output_path = temp_dir.path().join("lifetime.json");
979        assert!(output_path.exists());
980
981        // Verify file content
982        let content =
983            std::fs::read_to_string(&output_path).map_err(|e| ExportError(e.to_string()))?;
984        let json_data: serde_json::Value =
985            serde_json::from_str(&content).map_err(|e| SerializationError(e.to_string()))?;
986
987        assert!(json_data.get("metadata").is_some());
988        assert!(json_data.get("ownership_histories").is_some());
989
990        let metadata = &json_data["metadata"];
991        assert_eq!(metadata["export_version"].as_str().unwrap(), "2.0");
992        assert_eq!(
993            metadata["specification"].as_str().unwrap(),
994            "improve.md lifetime tracking"
995        );
996        assert_eq!(metadata["total_tracked_allocations"].as_u64().unwrap(), 2);
997
998        Ok(())
999    }
1000
1001    #[test]
1002    fn test_export_unsafe_ffi_analysis() -> TrackingResult<()> {
1003        use tempfile::TempDir;
1004
1005        let temp_dir = TempDir::new().map_err(|e| ExportError(e.to_string()))?;
1006        let exporter = EnhancedJsonExporter::default();
1007
1008        let unsafe_reports = vec![];
1009        let memory_passports = vec![];
1010
1011        exporter.export_unsafe_ffi_analysis(&temp_dir, &unsafe_reports, &memory_passports)?;
1012
1013        let output_path = temp_dir.path().join("unsafe_ffi.json");
1014        assert!(output_path.exists());
1015
1016        // Verify file content
1017        let content =
1018            std::fs::read_to_string(&output_path).map_err(|e| ExportError(e.to_string()))?;
1019        let json_data: serde_json::Value =
1020            serde_json::from_str(&content).map_err(|e| SerializationError(e.to_string()))?;
1021
1022        assert!(json_data.get("metadata").is_some());
1023        assert!(json_data.get("unsafe_reports").is_some());
1024        assert!(json_data.get("memory_passports").is_some());
1025
1026        let metadata = &json_data["metadata"];
1027        assert_eq!(metadata["export_version"].as_str().unwrap(), "2.0");
1028        assert_eq!(
1029            metadata["specification"].as_str().unwrap(),
1030            "improve.md unsafe FFI tracking"
1031        );
1032        assert_eq!(metadata["total_unsafe_reports"].as_u64().unwrap(), 0);
1033        assert_eq!(metadata["total_memory_passports"].as_u64().unwrap(), 0);
1034
1035        Ok(())
1036    }
1037
1038    #[test]
1039    fn test_export_enhanced_analysis_all_files() -> TrackingResult<()> {
1040        use tempfile::TempDir;
1041
1042        let temp_dir = TempDir::new().map_err(|e| ExportError(e.to_string()))?;
1043        let exporter = EnhancedJsonExporter::default();
1044        let memory_stats = create_test_memory_stats();
1045        let unsafe_reports = vec![];
1046        let memory_passports = vec![];
1047
1048        exporter.export_enhanced_analysis(
1049            &temp_dir,
1050            &memory_stats,
1051            &unsafe_reports,
1052            &memory_passports,
1053        )?;
1054
1055        // Verify all files were created
1056        assert!(temp_dir.path().join("memory_analysis.json").exists());
1057        assert!(temp_dir.path().join("lifetime.json").exists());
1058        assert!(temp_dir.path().join("unsafe_ffi.json").exists());
1059
1060        Ok(())
1061    }
1062
1063    #[test]
1064    fn test_export_enhanced_analysis_selective_files() -> TrackingResult<()> {
1065        use tempfile::TempDir;
1066
1067        let temp_dir = TempDir::new().map_err(|e| ExportError(e.to_string()))?;
1068        let config = ExportConfig {
1069            generate_lifetime_file: false,
1070            generate_unsafe_ffi_file: true,
1071            ..Default::default()
1072        };
1073        let exporter = EnhancedJsonExporter::new(config);
1074        let memory_stats = create_test_memory_stats();
1075        let unsafe_reports = vec![];
1076        let memory_passports = vec![];
1077
1078        exporter.export_enhanced_analysis(
1079            &temp_dir,
1080            &memory_stats,
1081            &unsafe_reports,
1082            &memory_passports,
1083        )?;
1084
1085        // Verify selective file creation
1086        assert!(temp_dir.path().join("memory_analysis.json").exists());
1087        assert!(!temp_dir.path().join("lifetime.json").exists()); // Should not exist
1088        assert!(temp_dir.path().join("unsafe_ffi.json").exists());
1089
1090        Ok(())
1091    }
1092
1093    #[test]
1094    fn test_write_json_file_pretty_print() -> TrackingResult<()> {
1095        use tempfile::TempDir;
1096
1097        let temp_dir = TempDir::new().map_err(|e| ExportError(e.to_string()))?;
1098        let config = ExportConfig {
1099            pretty_print: true,
1100            ..Default::default()
1101        };
1102        let exporter = EnhancedJsonExporter::new(config);
1103
1104        let test_data = serde_json::json!({
1105            "test": "value",
1106            "number": 42
1107        });
1108
1109        let output_path = temp_dir.path().join("pretty_test.json");
1110        exporter.write_json_file(&output_path, &test_data)?;
1111
1112        assert!(output_path.exists());
1113
1114        let content =
1115            std::fs::read_to_string(&output_path).map_err(|e| ExportError(e.to_string()))?;
1116
1117        // Pretty printed JSON should contain newlines and indentation
1118        assert!(content.contains('\n'));
1119        assert!(content.contains("  ")); // Indentation
1120
1121        Ok(())
1122    }
1123
1124    #[test]
1125    fn test_write_json_file_compact() -> TrackingResult<()> {
1126        use tempfile::TempDir;
1127
1128        let temp_dir = TempDir::new().map_err(|e| ExportError(e.to_string()))?;
1129        let config = ExportConfig {
1130            pretty_print: false,
1131            ..Default::default()
1132        };
1133        let exporter = EnhancedJsonExporter::new(config);
1134
1135        let test_data = serde_json::json!({
1136            "test": "value",
1137            "number": 42
1138        });
1139
1140        let output_path = temp_dir.path().join("compact_test.json");
1141        exporter.write_json_file(&output_path, &test_data)?;
1142
1143        assert!(output_path.exists());
1144
1145        let content =
1146            std::fs::read_to_string(&output_path).map_err(|e| ExportError(e.to_string()))?;
1147
1148        // Compact JSON should not contain extra whitespace
1149        assert!(!content.contains('\n'));
1150        assert!(!content.contains("  ")); // No indentation
1151
1152        Ok(())
1153    }
1154
1155    #[test]
1156    fn test_export_enhanced_json_convenience_function() -> TrackingResult<()> {
1157        use tempfile::TempDir;
1158
1159        let temp_dir = TempDir::new().map_err(|e| ExportError(e.to_string()))?;
1160        let memory_stats = create_test_memory_stats();
1161        let unsafe_reports = vec![];
1162        let memory_passports = vec![];
1163
1164        export_enhanced_json(&temp_dir, &memory_stats, &unsafe_reports, &memory_passports)?;
1165
1166        // Verify all files were created using default settings
1167        assert!(temp_dir.path().join("memory_analysis.json").exists());
1168        assert!(temp_dir.path().join("lifetime.json").exists());
1169        assert!(temp_dir.path().join("unsafe_ffi.json").exists());
1170
1171        Ok(())
1172    }
1173
1174    #[test]
1175    fn test_export_with_empty_allocations() -> TrackingResult<()> {
1176        use tempfile::TempDir;
1177
1178        let temp_dir = TempDir::new().map_err(|e| ExportError(e.to_string()))?;
1179        let exporter = EnhancedJsonExporter::default();
1180
1181        let empty_memory_stats = MemoryStats {
1182            total_allocations: 0,
1183            total_allocated: 0,
1184            active_allocations: 0,
1185            active_memory: 0,
1186            peak_allocations: 0,
1187            peak_memory: 0,
1188            total_deallocations: 0,
1189            total_deallocated: 0,
1190            leaked_allocations: 0,
1191            leaked_memory: 0,
1192            fragmentation_analysis: crate::core::types::FragmentationAnalysis::default(),
1193            lifecycle_stats: crate::core::types::ScopeLifecycleMetrics::default(),
1194            allocations: vec![], // Empty allocations
1195            system_library_stats: crate::core::types::SystemLibraryStats::default(),
1196            concurrency_analysis: crate::core::types::ConcurrencyAnalysis::default(),
1197        };
1198
1199        exporter.export_memory_analysis(&temp_dir, &empty_memory_stats)?;
1200
1201        let output_path = temp_dir.path().join("memory_analysis.json");
1202        assert!(output_path.exists());
1203
1204        // Verify file content with empty allocations
1205        let content =
1206            std::fs::read_to_string(&output_path).map_err(|e| ExportError(e.to_string()))?;
1207        let json_data: serde_json::Value =
1208            serde_json::from_str(&content).map_err(|e| SerializationError(e.to_string()))?;
1209
1210        let metadata = &json_data["metadata"];
1211        assert_eq!(metadata["total_allocations"].as_u64().unwrap(), 0);
1212
1213        let allocations = json_data["allocations"].as_array().unwrap();
1214        assert_eq!(allocations.len(), 0);
1215
1216        Ok(())
1217    }
1218
1219    #[test]
1220    fn test_export_with_large_dataset() -> TrackingResult<()> {
1221        use tempfile::TempDir;
1222
1223        let temp_dir = TempDir::new().map_err(|e| ExportError(e.to_string()))?;
1224        let exporter = EnhancedJsonExporter::default();
1225
1226        // Create a large dataset
1227        let mut large_allocations = Vec::new();
1228        for i in 0..1000 {
1229            let mut alloc = create_test_allocation(0x1000 + i * 0x100, 64 + i % 100);
1230            alloc.ownership_history_available = i % 2 == 0; // Half have ownership history
1231            large_allocations.push(alloc);
1232        }
1233
1234        let large_memory_stats = MemoryStats {
1235            total_allocations: 1000,
1236            total_allocated: 114000, // Approximate
1237            active_allocations: 1000,
1238            active_memory: 114000,
1239            peak_allocations: 1000,
1240            peak_memory: 114000,
1241            total_deallocations: 0,
1242            total_deallocated: 0,
1243            leaked_allocations: 0,
1244            leaked_memory: 0,
1245            fragmentation_analysis: crate::core::types::FragmentationAnalysis::default(),
1246            lifecycle_stats: crate::core::types::ScopeLifecycleMetrics::default(),
1247            allocations: large_allocations,
1248            system_library_stats: crate::core::types::SystemLibraryStats::default(),
1249            concurrency_analysis: crate::core::types::ConcurrencyAnalysis::default(),
1250        };
1251
1252        let unsafe_reports = vec![];
1253        let memory_passports = vec![];
1254
1255        exporter.export_enhanced_analysis(
1256            &temp_dir,
1257            &large_memory_stats,
1258            &unsafe_reports,
1259            &memory_passports,
1260        )?;
1261
1262        // Verify all files were created
1263        assert!(temp_dir.path().join("memory_analysis.json").exists());
1264        assert!(temp_dir.path().join("lifetime.json").exists());
1265        assert!(temp_dir.path().join("unsafe_ffi.json").exists());
1266
1267        // Verify memory analysis content
1268        let memory_content = std::fs::read_to_string(temp_dir.path().join("memory_analysis.json"))
1269            .map_err(|e| ExportError(e.to_string()))?;
1270        let memory_json: serde_json::Value =
1271            serde_json::from_str(&memory_content).map_err(|e| SerializationError(e.to_string()))?;
1272
1273        assert_eq!(
1274            memory_json["metadata"]["total_allocations"]
1275                .as_u64()
1276                .unwrap(),
1277            1000
1278        );
1279
1280        // Verify lifetime data content
1281        let lifetime_content = std::fs::read_to_string(temp_dir.path().join("lifetime.json"))
1282            .map_err(|e| ExportError(e.to_string()))?;
1283        let lifetime_json: serde_json::Value = serde_json::from_str(&lifetime_content)
1284            .map_err(|e| SerializationError(e.to_string()))?;
1285
1286        assert_eq!(
1287            lifetime_json["metadata"]["total_tracked_allocations"]
1288                .as_u64()
1289                .unwrap(),
1290            500
1291        ); // Half have ownership history
1292
1293        Ok(())
1294    }
1295
1296    #[test]
1297    fn test_export_with_complex_allocations() -> TrackingResult<()> {
1298        use tempfile::TempDir;
1299
1300        let temp_dir = TempDir::new().map_err(|e| ExportError(e.to_string()))?;
1301        let exporter = EnhancedJsonExporter::default();
1302
1303        // Create complex allocations with various characteristics
1304        let mut complex_allocations = Vec::new();
1305
1306        // Allocation with ownership history
1307        let mut alloc1 = create_test_allocation(0x1000, 1024);
1308        alloc1.ownership_history_available = true;
1309        alloc1.borrow_count = 5;
1310        alloc1.is_leaked = false;
1311        alloc1.var_name = Some("complex_vector".to_string());
1312        alloc1.type_name = Some("Vec<String>".to_string());
1313        complex_allocations.push(alloc1);
1314
1315        // Leaked allocation
1316        let mut alloc2 = create_test_allocation(0x2000, 512);
1317        alloc2.is_leaked = true;
1318        alloc2.var_name = Some("leaked_data".to_string());
1319        alloc2.type_name = Some("Box<[u8]>".to_string());
1320        complex_allocations.push(alloc2);
1321
1322        // High borrow count allocation
1323        let mut alloc3 = create_test_allocation(0x3000, 256);
1324        alloc3.borrow_count = 100;
1325        alloc3.var_name = Some("shared_resource".to_string());
1326        alloc3.type_name = Some("Rc<RefCell<Data>>".to_string());
1327        complex_allocations.push(alloc3);
1328
1329        let memory_stats = MemoryStats {
1330            total_allocations: 3,
1331            total_allocated: 1792,
1332            active_allocations: 3,
1333            active_memory: 1792,
1334            peak_allocations: 3,
1335            peak_memory: 1792,
1336            total_deallocations: 0,
1337            total_deallocated: 0,
1338            leaked_allocations: 1,
1339            leaked_memory: 512,
1340            fragmentation_analysis: crate::core::types::FragmentationAnalysis::default(),
1341            lifecycle_stats: crate::core::types::ScopeLifecycleMetrics::default(),
1342            allocations: complex_allocations,
1343            system_library_stats: crate::core::types::SystemLibraryStats::default(),
1344            concurrency_analysis: crate::core::types::ConcurrencyAnalysis::default(),
1345        };
1346
1347        let unsafe_reports = vec![];
1348        let memory_passports = vec![];
1349
1350        exporter.export_enhanced_analysis(
1351            &temp_dir,
1352            &memory_stats,
1353            &unsafe_reports,
1354            &memory_passports,
1355        )?;
1356
1357        // Verify memory analysis content
1358        let memory_content = std::fs::read_to_string(temp_dir.path().join("memory_analysis.json"))
1359            .map_err(|e| ExportError(e.to_string()))?;
1360        let memory_json: serde_json::Value =
1361            serde_json::from_str(&memory_content).map_err(|e| SerializationError(e.to_string()))?;
1362
1363        let allocations = memory_json["allocations"].as_array().unwrap();
1364        assert_eq!(allocations.len(), 3);
1365
1366        // Check for leaked allocation
1367        let leaked_alloc = allocations
1368            .iter()
1369            .find(|a| a["is_leaked"].as_bool().unwrap_or(false));
1370        assert!(leaked_alloc.is_some());
1371        assert_eq!(leaked_alloc.unwrap()["size"].as_u64().unwrap(), 512);
1372
1373        // Check for high borrow count allocation
1374        let high_borrow = allocations
1375            .iter()
1376            .find(|a| a["borrow_count"].as_u64().unwrap_or(0) == 100);
1377        assert!(high_borrow.is_some());
1378
1379        Ok(())
1380    }
1381
1382    #[test]
1383    fn test_export_with_unsafe_reports() -> TrackingResult<()> {
1384        use tempfile::TempDir;
1385
1386        let temp_dir = TempDir::new().map_err(|e| ExportError(e.to_string()))?;
1387        let exporter = EnhancedJsonExporter::default();
1388
1389        let allocations = vec![create_test_allocation(0x1000, 64)];
1390        let mut memory_stats = create_test_memory_stats();
1391        memory_stats.allocations = allocations;
1392
1393        // Create empty unsafe reports (since UnsafeReport structure is complex)
1394        let unsafe_reports = vec![];
1395
1396        let memory_passports = vec![];
1397
1398        exporter.export_enhanced_analysis(
1399            &temp_dir,
1400            &memory_stats,
1401            &unsafe_reports,
1402            &memory_passports,
1403        )?;
1404
1405        // Verify unsafe FFI content (with empty reports)
1406        let unsafe_content = std::fs::read_to_string(temp_dir.path().join("unsafe_ffi.json"))
1407            .map_err(|e| ExportError(e.to_string()))?;
1408        let unsafe_json: serde_json::Value =
1409            serde_json::from_str(&unsafe_content).map_err(|e| SerializationError(e.to_string()))?;
1410
1411        let reports = unsafe_json["unsafe_reports"].as_array().unwrap();
1412        assert_eq!(reports.len(), 0); // Empty reports since we simplified the test
1413
1414        Ok(())
1415    }
1416
1417    #[test]
1418    fn test_export_error_handling() -> TrackingResult<()> {
1419        let exporter = EnhancedJsonExporter::default();
1420
1421        // Use a path that should fail on all platforms
1422        let invalid_path = if cfg!(windows) {
1423            std::path::Path::new("C:\\invalid<>|path") // Invalid Windows characters
1424        } else {
1425            std::path::Path::new("/dev/null/invalid") // /dev/null is not a directory
1426        };
1427
1428        let allocations = vec![create_test_allocation(0x1000, 64)];
1429        let mut memory_stats = create_test_memory_stats();
1430        memory_stats.allocations = allocations;
1431        let unsafe_reports = vec![];
1432        let memory_passports = vec![];
1433
1434        let result = exporter.export_enhanced_analysis(
1435            invalid_path,
1436            &memory_stats,
1437            &unsafe_reports,
1438            &memory_passports,
1439        );
1440
1441        assert!(
1442            result.is_err(),
1443            "Expected error when exporting to invalid path: {:?}",
1444            invalid_path
1445        );
1446
1447        Ok(())
1448    }
1449
1450    #[test]
1451    fn test_json_serialization_edge_cases() -> TrackingResult<()> {
1452        use tempfile::TempDir;
1453
1454        let temp_dir = TempDir::new().map_err(|e| ExportError(e.to_string()))?;
1455        let exporter = EnhancedJsonExporter::default();
1456
1457        // Create allocations with edge case values
1458        let mut edge_allocations = Vec::new();
1459
1460        // Zero-sized allocation
1461        let mut alloc1 = create_test_allocation(0x1000, 0);
1462        alloc1.var_name = Some("".to_string()); // Empty string
1463        alloc1.type_name = Some("()".to_string()); // Unit type
1464        edge_allocations.push(alloc1);
1465
1466        // Very large allocation
1467        let mut alloc2 = create_test_allocation(0x2000, 1000000); // Use reasonable size instead of usize::MAX
1468        alloc2.borrow_count = 1000; // Use reasonable count instead of usize::MAX
1469        alloc2.var_name = Some("very_long_variable_name_".repeat(10)); // Shorter repeat
1470        alloc2.type_name = Some("VeryComplexType<T, U, V>".to_string());
1471        edge_allocations.push(alloc2);
1472
1473        // Allocation with special characters
1474        let mut alloc3 = create_test_allocation(0x3000, 256);
1475        alloc3.var_name = Some("var_with_unicode_🦀_and_symbols".to_string());
1476        alloc3.type_name = Some("Type\"With'Quotes<&str>".to_string());
1477        edge_allocations.push(alloc3);
1478
1479        let memory_stats = MemoryStats {
1480            total_allocations: 3,
1481            total_allocated: 1000256, // Sum of allocation sizes
1482            active_allocations: 3,
1483            active_memory: 1000256,
1484            peak_allocations: 3,
1485            peak_memory: 1000256,
1486            total_deallocations: 0,
1487            total_deallocated: 0,
1488            leaked_allocations: 0,
1489            leaked_memory: 0,
1490            fragmentation_analysis: crate::core::types::FragmentationAnalysis::default(),
1491            lifecycle_stats: crate::core::types::ScopeLifecycleMetrics::default(),
1492            allocations: edge_allocations,
1493            system_library_stats: crate::core::types::SystemLibraryStats::default(),
1494            concurrency_analysis: crate::core::types::ConcurrencyAnalysis::default(),
1495        };
1496
1497        let unsafe_reports = vec![];
1498        let memory_passports = vec![];
1499
1500        // Should handle edge cases gracefully
1501        let result = exporter.export_enhanced_analysis(
1502            &temp_dir,
1503            &memory_stats,
1504            &unsafe_reports,
1505            &memory_passports,
1506        );
1507
1508        assert!(result.is_ok());
1509
1510        // Verify files were created
1511        assert!(temp_dir.path().join("memory_analysis.json").exists());
1512
1513        // Verify JSON is valid
1514        let memory_content = std::fs::read_to_string(temp_dir.path().join("memory_analysis.json"))
1515            .map_err(|e| ExportError(e.to_string()))?;
1516        let memory_json: serde_json::Value =
1517            serde_json::from_str(&memory_content).map_err(|e| SerializationError(e.to_string()))?;
1518
1519        let allocations = memory_json["allocations"].as_array().unwrap();
1520        assert_eq!(allocations.len(), 3);
1521
1522        // Check zero-sized allocation
1523        let zero_alloc = allocations
1524            .iter()
1525            .find(|a| a["size"].as_u64().unwrap() == 0);
1526        assert!(zero_alloc.is_some());
1527
1528        // Check allocation with special characters
1529        let unicode_alloc = allocations
1530            .iter()
1531            .find(|a| a["var_name"].as_str().unwrap_or("").contains("🦀"));
1532        assert!(unicode_alloc.is_some());
1533
1534        Ok(())
1535    }
1536}