memscope_rs/export/binary/
binary_template_engine.rs

1//! Binary template engine for processing binary-specific HTML templates
2//!
3//! This module provides a specialized template engine that processes the binary_dashboard.html
4//! template with data directly from binary sources, independent of the JSON → HTML workflow.
5
6use crate::export::binary::binary_html_writer::BinaryTemplateData;
7use crate::export::binary::error::BinaryExportError;
8use crate::export::binary::template_resource_manager::{
9    create_template_data, ResourceConfig, TemplateResourceManager,
10};
11
12use std::collections::HashMap;
13use std::time::Instant;
14
15/// Configuration for the binary template engine
16#[derive(Debug, Clone)]
17pub struct BinaryTemplateEngineConfig {
18    /// Enable template caching for better performance
19    pub enable_cache: bool,
20
21    /// Enable template precompilation
22    pub enable_precompilation: bool,
23
24    /// Enable data compression for large datasets
25    pub enable_data_compression: bool,
26
27    /// Maximum template cache size in MB
28    pub max_cache_size_mb: usize,
29
30    /// Template processing timeout in seconds
31    pub processing_timeout_secs: u64,
32}
33
34impl Default for BinaryTemplateEngineConfig {
35    fn default() -> Self {
36        Self {
37            enable_cache: true,
38            enable_precompilation: true,
39            enable_data_compression: false,
40            max_cache_size_mb: 10,
41            processing_timeout_secs: 30,
42        }
43    }
44}
45
46/// Binary template engine for processing binary-specific templates
47pub struct BinaryTemplateEngine {
48    /// Template resource manager
49    resource_manager: TemplateResourceManager,
50
51    /// Resource configuration
52    resource_config: ResourceConfig,
53
54    /// Configuration
55    config: BinaryTemplateEngineConfig,
56
57    /// Performance statistics
58    last_render_time_ms: u64,
59
60    /// Template processing statistics
61    templates_processed: u64,
62
63    /// Cache hit count
64    cache_hits: u64,
65}
66
67impl BinaryTemplateEngine {
68    /// Create a new binary template engine with default configuration
69    pub fn new() -> Result<Self, BinaryExportError> {
70        Self::with_config(BinaryTemplateEngineConfig::default())
71    }
72
73    /// Create a new binary template engine with custom configuration
74    pub fn with_config(config: BinaryTemplateEngineConfig) -> Result<Self, BinaryExportError> {
75        let resource_manager = TemplateResourceManager::new("templates")?;
76        let resource_config = ResourceConfig {
77            embed_css: true,
78            embed_js: true,
79            embed_svg: true,
80            minify_resources: config.enable_data_compression,
81            custom_paths: HashMap::new(),
82        };
83
84        tracing::debug!(
85            "BinaryTemplateEngine configured with cache: {}, precompilation: {}",
86            config.enable_cache,
87            config.enable_precompilation
88        );
89
90        let engine = Self {
91            resource_manager,
92            resource_config,
93            config,
94            last_render_time_ms: 0,
95            templates_processed: 0,
96            cache_hits: 0,
97        };
98
99        Ok(engine)
100    }
101
102    /// Render the binary dashboard template with the provided data
103    pub fn render_binary_template(
104        &mut self,
105        template_data: &BinaryTemplateData,
106    ) -> Result<String, BinaryExportError> {
107        let render_start = Instant::now();
108
109        // Optimize for large datasets with pagination
110        let optimized_data = self.optimize_template_data_for_size(template_data)?;
111
112        // Convert template data to JSON for injection
113        let json_data = self.serialize_template_data(&optimized_data)?;
114
115        // Debug: Log the first 500 characters of JSON data
116        tracing::info!(
117            "JSON data preview: {}",
118            &json_data[..json_data.len().min(500)]
119        );
120        tracing::info!("JSON data length: {} bytes", json_data.len());
121
122        // Create template data for resource manager
123        let mut custom_data = HashMap::new();
124
125        // Add processing time and other common placeholders
126        custom_data.insert(
127            "PROCESSING_TIME".to_string(),
128            template_data.processing_time_ms.to_string(),
129        );
130        custom_data.insert("SVG_IMAGES".to_string(), self.load_svg_images()?);
131
132        // Add analysis data to custom data if available
133        if let Some(ref complex_types) = template_data.complex_types {
134            let complex_types_json = serde_json::to_string(complex_types).map_err(|e| {
135                BinaryExportError::SerializationError(format!(
136                    "Complex types serialization failed: {e}",
137                ))
138            })?;
139            custom_data.insert("complex_types".to_string(), complex_types_json);
140        }
141
142        if let Some(ref unsafe_ffi) = template_data.unsafe_ffi {
143            let ffi_json = serde_json::to_string(unsafe_ffi).map_err(|e| {
144                BinaryExportError::SerializationError(format!(
145                    "FFI safety serialization failed: {e}",
146                ))
147            })?;
148            custom_data.insert("unsafe_ffi".to_string(), ffi_json);
149        }
150
151        if let Some(ref variable_relationships) = template_data.variable_relationships {
152            let relationships_json =
153                serde_json::to_string(variable_relationships).map_err(|e| {
154                    BinaryExportError::SerializationError(format!(
155                        "Variable relationships serialization failed: {e}",
156                    ))
157                })?;
158            custom_data.insert("variable_relationships".to_string(), relationships_json);
159        }
160
161        let mut resource_template_data =
162            create_template_data(&template_data.project_name, &json_data, custom_data);
163
164        // Ensure JS and CSS content are properly set
165        resource_template_data.js_content = self._get_embedded_js();
166        resource_template_data.css_content = self._get_embedded_css();
167
168        // Process template with resource manager - use the same template as JSON→HTML
169        let html_content = self.resource_manager.process_template(
170            "clean_dashboard.html",
171            &resource_template_data,
172            &self.resource_config,
173        )?;
174
175        // Update statistics
176        self.last_render_time_ms = render_start.elapsed().as_millis() as u64;
177        self.templates_processed += 1;
178
179        Ok(html_content)
180    }
181
182    /// Serialize template data to JSON format optimized for template compatibility
183    fn serialize_template_data(
184        &self,
185        data: &BinaryTemplateData,
186    ) -> Result<String, BinaryExportError> {
187        use serde_json::json;
188
189        // Ultra-fast allocation data generation - minimal processing
190        let allocations_json: Vec<serde_json::Value> = data
191            .allocations
192            .iter()
193            .take(50) // Even smaller for maximum speed
194            .map(|alloc| {
195                // Pre-format pointer to avoid runtime formatting
196                let ptr_str = format!("0x{:x}", alloc.ptr);
197                json!({
198                    "id": alloc.id,
199                    "size": alloc.size,
200                    "type_name": alloc.type_name,
201                    "scope_name": alloc.scope_name,
202                    "var_name": alloc.var_name,
203                    "ptr": ptr_str,
204                    "timestamp_alloc": alloc.timestamp_alloc,
205                    "is_active": alloc.is_active,
206                    "thread_id": alloc.thread_id,
207                    "borrow_count": alloc.borrow_count,
208                    "is_leaked": alloc.is_leaked,
209                    "lifetime_ms": alloc.lifetime_ms
210                })
211            })
212            .collect();
213
214        // Generate ultra-minimal data for charts - maximum speed
215        let memory_timeline = if data.allocations.len() > 1000 {
216            // For large datasets, use minimal timeline
217            vec![
218                json!({"timestamp": 0, "memory_usage": 0, "allocation_count": 0}),
219                json!({"timestamp": 1000000, "memory_usage": data.total_memory_usage, "allocation_count": data.allocations.len()}),
220            ]
221        } else {
222            self.generate_fast_timeline_data(&data.allocations)
223        };
224
225        let size_distribution = self.generate_fast_size_distribution(&data.allocations);
226        let lifecycle_events = if data.allocations.len() > 500 {
227            // Skip lifecycle events for large datasets
228            vec![]
229        } else {
230            self.generate_fast_lifecycle_events(&data.allocations)
231        };
232
233        // Build comprehensive dashboard data matching JSON→HTML format exactly
234        let mut dashboard_data = json!({
235            "memory_analysis": {
236                "allocations": allocations_json,
237                "stats": {
238                    "total_allocations": data.allocations.len(),
239                    "active_allocations": data.active_allocations_count,
240                    "total_memory": data.total_memory_usage,
241                    "active_memory": data.total_memory_usage
242                },
243                "memory_timeline": memory_timeline,
244                "size_distribution": size_distribution,
245                "fragmentation_analysis": {
246                    "total_blocks": data.allocations.len(),
247                    "fragmentation_score": 15,
248                    "largest_block": data.allocations.iter().map(|a| a.size).max().unwrap_or(0),
249                    "gaps": 0,
250                    "total_gap_size": 0,
251                    "analysis": "Low fragmentation detected"
252                },
253                "growth_trends": {
254                    "peak_memory": data.peak_memory_usage,
255                    "current_memory": data.total_memory_usage,
256                    "growth_rate": 0,
257                    "allocation_rate": data.allocations.len() as u64,
258                    "time_points": memory_timeline,
259                    "analysis": "Stable memory usage"
260                },
261                "visualization_ready": true
262            },
263            "lifetime": {
264                "lifecycle_events": lifecycle_events,
265                "variable_groups": [],
266                "user_variables_count": data.allocations.iter().filter(|a| a.var_name.is_some()).count(),
267                "visualization_ready": true
268            },
269            "performance": {
270                "memory_performance": {
271                    "active_memory": data.total_memory_usage,
272                    "peak_memory": data.peak_memory_usage,
273                    "total_allocated": data.total_memory_usage
274                },
275                "allocation_distribution": {
276                    "tiny": data.allocations.iter().filter(|a| a.size < 100).count(),
277                    "small": data.allocations.iter().filter(|a| a.size >= 100 && a.size < 1024).count(),
278                    "medium": data.allocations.iter().filter(|a| a.size >= 1024 && a.size < 10240).count(),
279                    "large": data.allocations.iter().filter(|a| a.size >= 10240 && a.size < 102400).count(),
280                    "massive": data.allocations.iter().filter(|a| a.size >= 102400).count()
281                }
282            }
283        });
284
285        // Generate complex types analysis from allocations
286        let mut smart_pointers = Vec::new();
287        let mut collections = Vec::new();
288        let mut generic_types = Vec::new();
289        let mut primitive_types = Vec::new();
290
291        for alloc in &data.allocations {
292            let type_name = &alloc.type_name;
293            let type_info = json!({
294                "type_name": type_name,
295                "count": 1,
296                "total_size": alloc.size,
297                "complexity_score": if type_name.contains('<') { 3 } else { 1 }
298            });
299
300            if type_name.contains("Box<")
301                || type_name.contains("Rc<")
302                || type_name.contains("Arc<")
303                || type_name.contains("RefCell<")
304            {
305                smart_pointers.push(type_info);
306            } else if type_name.contains("Vec<")
307                || type_name.contains("HashMap<")
308                || type_name.contains("BTreeMap<")
309                || type_name.contains("HashSet<")
310                || type_name.contains("String")
311            {
312                collections.push(type_info);
313            } else if type_name.contains('<') && type_name.contains('>') {
314                generic_types.push(type_info);
315            } else if ![
316                "i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "f32", "f64", "bool", "char",
317            ]
318            .contains(&type_name.as_str())
319            {
320                primitive_types.push(type_info);
321            }
322        }
323
324        dashboard_data["complex_types"] = json!({
325            "categorized_types": {
326                "smart_pointers": smart_pointers,
327                "collections": collections,
328                "generic_types": generic_types,
329                "trait_objects": [],
330                "primitive_types": primitive_types
331            },
332            "type_complexity": {},
333            "memory_usage_by_type": {},
334            "summary": {
335                "total_complex_types": smart_pointers.len() + collections.len() + generic_types.len(),
336                "smart_pointers_count": smart_pointers.len(),
337                "collections_count": collections.len(),
338                "generic_types_count": generic_types.len(),
339                "generic_type_count": generic_types.len()
340            }
341        });
342
343        // Generate unsafe FFI analysis from allocations - matching snapshot_unsafe_ffi.json format
344        let mut unsafe_operations = Vec::new();
345        let mut security_hotspots = Vec::new();
346        let mut enhanced_ffi_data = Vec::new();
347        let mut boundary_events = Vec::new();
348
349        for alloc in &data.allocations {
350            // Check for potentially unsafe operations based on type patterns and size
351            // More comprehensive detection for demonstration purposes
352            let is_unsafe = alloc.type_name.contains("*mut") || 
353                           alloc.type_name.contains("*const") || 
354                           alloc.type_name.contains("unsafe") ||
355                           alloc.type_name.contains("libc") ||
356                           alloc.type_name.contains("system_type") || // System types often indicate FFI
357                           alloc.type_name.contains("ffi") ||
358                           alloc.type_name.contains("extern") ||
359                           alloc.size > 1024*1024 || // Large allocations
360                           alloc.var_name.as_ref().is_some_and(|name| name.contains("ffi") || name.contains("unsafe")) ||
361                           // Include some common patterns that might indicate unsafe operations
362                           (alloc.size > 100*1024 && !alloc.type_name.contains("Vec") && !alloc.type_name.contains("String"));
363
364            if is_unsafe {
365                let risk_level = if alloc.size > 10 * 1024 * 1024 {
366                    "High"
367                } else if alloc.size > 1024 * 1024 {
368                    "Medium"
369                } else {
370                    "Low"
371                };
372
373                let operation = json!({
374                    "ptr": format!("0x{:x}", alloc.ptr),
375                    "operation_type": if alloc.type_name.contains("*mut") { "Raw Pointer Mutation" }
376                                     else if alloc.type_name.contains("*const") { "Raw Pointer Access" }
377                                     else if alloc.type_name.contains("libc") { "FFI Call" }
378                                     else if alloc.size > 1024*1024 { "Large Allocation" }
379                                     else { "Unsafe Operation" },
380                    "risk_level": risk_level,
381                    "location": alloc.var_name.as_ref().unwrap_or(&"unknown".to_string()).clone(),
382                    "timestamp": alloc.timestamp_alloc,
383                    "size": alloc.size,
384                    "safety_violations": if alloc.size > 10*1024*1024 { 3 }
385                                        else if alloc.size > 1024*1024 { 2 }
386                                        else { 1 }
387                });
388                unsafe_operations.push(operation.clone());
389
390                // Add to enhanced FFI data with more detailed information
391                enhanced_ffi_data.push(json!({
392                    "ptr": format!("0x{:x}", alloc.ptr),
393                    "size": alloc.size,
394                    "var_name": alloc.var_name,
395                    "type_name": alloc.type_name,
396                    "timestamp_alloc": alloc.timestamp_alloc,
397                    "thread_id": alloc.thread_id,
398                    "stack_trace": ["no_stack_trace_available"],
399                    "runtime_state": {"status": "not_analyzed"},
400                    "ffi_tracked": alloc.type_name.contains("libc") || alloc.type_name.contains("*"),
401                    "safety_violations": if alloc.size > 10*1024*1024 { 3 }
402                                        else if alloc.size > 1024*1024 { 2 }
403                                        else { 1 }
404                }));
405
406                // Add boundary events for FFI-related allocations
407                if alloc.type_name.contains("libc") || alloc.type_name.contains("*") {
408                    boundary_events.push(json!({
409                        "event_type": if alloc.type_name.contains("libc") { "FfiToRust" } else { "RustToFfi" },
410                        "timestamp": alloc.timestamp_alloc,
411                        "from_context": if alloc.type_name.contains("libc") { "libc" } else { "rust_main" },
412                        "to_context": if alloc.type_name.contains("libc") { "rust_main" } else { "potential_ffi_target" },
413                        "stack": [json!({
414                            "function_name": "current_function",
415                            "file_name": "src/unsafe_ffi_tracker.rs",
416                            "line_number": 42,
417                            "is_unsafe": true
418                        })]
419                    }));
420                }
421
422                // Create security hotspots for high-risk operations
423                if risk_level == "High" || alloc.size > 1024 * 1024 {
424                    security_hotspots.push(json!({
425                        "location": alloc.var_name.as_ref().unwrap_or(&"unknown".to_string()).clone(),
426                        "description": format!("High-risk {} operation detected", operation["operation_type"]),
427                        "violation_count": operation["safety_violations"],
428                        "risk_score": if risk_level == "High" { 8.5 } else { 6.0 }
429                    }));
430                }
431            }
432        }
433
434        let total_violations = unsafe_operations
435            .iter()
436            .map(|op| op["safety_violations"].as_u64().unwrap_or(0))
437            .sum::<u64>();
438
439        let risk_level = if total_violations > 20 {
440            "High"
441        } else if total_violations > 10 {
442            "Medium"
443        } else {
444            "Low"
445        };
446
447        dashboard_data["unsafe_ffi"] = json!({
448            "summary": {
449                "total_risk_items": enhanced_ffi_data.len(),
450                "unsafe_count": unsafe_operations.len(),
451                "ffi_count": enhanced_ffi_data.iter().filter(|item| item["ffi_tracked"].as_bool().unwrap_or(false)).count(),
452                "safety_violations": total_violations
453            },
454            "enhanced_ffi_data": enhanced_ffi_data,
455            "safety_violations": unsafe_operations,
456            "boundary_events": boundary_events,
457            "comprehensive_stats": {
458                "unsafe_allocations": unsafe_operations.len(),
459                "ffi_allocations": enhanced_ffi_data.iter().filter(|item| item["ffi_tracked"].as_bool().unwrap_or(false)).count(),
460                "boundary_crossings": boundary_events.len(),
461                "safety_violations": total_violations,
462                "unsafe_memory": enhanced_ffi_data.iter().map(|item| item["size"].as_u64().unwrap_or(0)).sum::<u64>()
463            },
464            "language_interactions": [],
465            "safety_analysis": {
466                "risk_level": risk_level,
467                "total_violations": total_violations,
468                "security_hotspots": security_hotspots
469            },
470            "visualization_ready": true
471        });
472
473        // Generate basic variable relationships from allocations
474        let allocations_sample = data.allocations.iter().take(20).collect::<Vec<_>>();
475        let mut nodes = Vec::new();
476        let mut links = Vec::new();
477
478        for (i, alloc) in allocations_sample.iter().enumerate() {
479            if let Some(var_name) = &alloc.var_name {
480                if !var_name.is_empty() && !var_name.starts_with("__") {
481                    nodes.push(json!({
482                        "id": format!("var_{i}"),
483                        "name": var_name,
484                        "type": &alloc.type_name,
485                        "size": alloc.size,
486                        "group": i % 4 + 1
487                    }));
488
489                    // Create some basic relationships based on type similarity
490                    if i > 0 {
491                        let prev_alloc = allocations_sample[i - 1];
492                        if alloc.type_name == prev_alloc.type_name {
493                            links.push(json!({
494                                "source": format!("var_{}", i-1),
495                                "target": format!("var_{i}"),
496                                "strength": 0.8,
497                                "type": "type_similarity"
498                            }));
499                        }
500                    }
501                }
502            }
503        }
504
505        dashboard_data["variable_relationships"] = json!({
506            "nodes": nodes,
507            "edges": links,
508            "summary": {
509                "total_variables": nodes.len(),
510                "total_relationships": links.len(),
511                "relationship_density": if nodes.len() > 1 {
512                    links.len() as f64 / (nodes.len() * (nodes.len() - 1) / 2) as f64
513                } else {
514                    0.0
515                }
516            }
517        });
518
519        // Add security_violations structure to match JSON→HTML format
520        dashboard_data["security_violations"] = json!({
521            "metadata": {
522                "total_violations": total_violations
523            },
524            "violation_reports": unsafe_operations,
525            "security_summary": {
526                "security_analysis_summary": {
527                    "total_violations": total_violations,
528                    "severity_breakdown": {
529                        "critical": unsafe_operations.iter().filter(|op| op["risk_level"] == "High").count(),
530                        "high": unsafe_operations.iter().filter(|op| op["risk_level"] == "Medium").count(),
531                        "medium": unsafe_operations.iter().filter(|op| op["risk_level"] == "Low").count(),
532                        "low": 0,
533                        "info": 0
534                    }
535                }
536            }
537        });
538
539        serde_json::to_string(&dashboard_data).map_err(|e| {
540            BinaryExportError::SerializationError(format!("JSON serialization failed: {e}"))
541        })
542    }
543
544    /// Process template placeholders with actual data
545    fn _process_template_placeholders(
546        &self,
547        template: &str,
548        template_data: &BinaryTemplateData,
549        json_data: &str,
550        css_content: &str,
551        js_content: &str,
552    ) -> Result<String, BinaryExportError> {
553        let mut html_content = template.to_string();
554
555        // Replace basic placeholders
556        html_content = html_content.replace("{{PROJECT_NAME}}", &template_data.project_name);
557        html_content = html_content.replace("{{BINARY_DATA}}", json_data);
558        html_content = html_content.replace("{{CSS_CONTENT}}", css_content);
559        html_content = html_content.replace("{{JS_CONTENT}}", js_content);
560        html_content = html_content.replace(
561            "{{GENERATION_TIME}}",
562            &chrono::Utc::now()
563                .format("%Y-%m-%d %H:%M:%S UTC")
564                .to_string(),
565        );
566        html_content = html_content.replace(
567            "{{PROCESSING_TIME}}",
568            &template_data.processing_time_ms.to_string(),
569        );
570
571        // Replace performance-specific placeholders
572        let throughput = self.calculate_throughput(template_data);
573        html_content = html_content.replace("{{THROUGHPUT}}", &throughput.to_string());
574
575        Ok(html_content)
576    }
577
578    /// Calculate processing throughput
579    #[allow(dead_code)]
580    fn calculate_throughput(&self, data: &BinaryTemplateData) -> f64 {
581        if data.processing_time_ms == 0 {
582            0.0
583        } else {
584            (data.allocations.len() as f64 * 1000.0) / data.processing_time_ms as f64
585        }
586    }
587
588    /// Generate ultra-fast timeline data - minimal processing
589    fn generate_fast_timeline_data(
590        &self,
591        allocations: &[crate::export::binary::binary_html_writer::BinaryAllocationData],
592    ) -> Vec<serde_json::Value> {
593        use serde_json::json;
594
595        // Only generate 5 data points for maximum speed
596        if allocations.is_empty() {
597            return vec![];
598        }
599
600        let len = allocations.len();
601        let total_memory: u64 = allocations.iter().map(|a| a.size as u64).sum();
602
603        // Create simple linear progression
604        vec![
605            json!({"timestamp": 0, "memory_usage": 0, "allocation_count": 0}),
606            json!({"timestamp": 250000, "memory_usage": total_memory / 4, "allocation_count": len / 4}),
607            json!({"timestamp": 500000, "memory_usage": total_memory / 2, "allocation_count": len / 2}),
608            json!({"timestamp": 750000, "memory_usage": total_memory * 3 / 4, "allocation_count": len * 3 / 4}),
609            json!({"timestamp": 1000000, "memory_usage": total_memory, "allocation_count": len}),
610        ]
611    }
612
613    /// Generate ultra-fast size distribution - minimal sampling
614    fn generate_fast_size_distribution(
615        &self,
616        allocations: &[crate::export::binary::binary_html_writer::BinaryAllocationData],
617    ) -> Vec<serde_json::Value> {
618        use serde_json::json;
619
620        if allocations.is_empty() {
621            return vec![];
622        }
623
624        // Ultra-fast estimation - sample only first 20 allocations
625        let sample_size = allocations.len().min(20);
626        let mut small = 0u64;
627        let mut medium = 0u64;
628        let mut large = 0u64;
629        let mut huge = 0u64;
630
631        for alloc in allocations.iter().take(sample_size) {
632            match alloc.size {
633                0..=1024 => small += 1,
634                1025..=102400 => medium += 1,
635                102401..=1048576 => large += 1,
636                _ => huge += 1,
637            }
638        }
639
640        // Scale up the sample to estimate full distribution
641        let scale_factor = allocations.len() as u64 / sample_size as u64;
642
643        vec![
644            json!({"size_range": "0-1KB", "count": small * scale_factor, "total_size": small * scale_factor * 512}),
645            json!({"size_range": "1-100KB", "count": medium * scale_factor, "total_size": medium * scale_factor * 50000}),
646            json!({"size_range": "100KB-1MB", "count": large * scale_factor, "total_size": large * scale_factor * 500000}),
647            json!({"size_range": ">1MB", "count": huge * scale_factor, "total_size": huge * scale_factor * 2000000}),
648        ]
649    }
650
651    /// Generate fast lifecycle events - minimal data
652    fn generate_fast_lifecycle_events(
653        &self,
654        allocations: &[crate::export::binary::binary_html_writer::BinaryAllocationData],
655    ) -> Vec<serde_json::Value> {
656        use serde_json::json;
657
658        // Only take every 100th allocation and limit to 20 events
659        allocations
660            .iter()
661            .step_by(100)
662            .take(20)
663            .map(|alloc| {
664                json!({
665                    "id": alloc.id,
666                    "event_type": if alloc.is_active { "Allocation" } else { "Deallocation" },
667                    "timestamp": alloc.timestamp_alloc,
668                    "size": alloc.size
669                })
670            })
671            .collect()
672    }
673
674    /// Count unique scopes in allocations
675    #[allow(dead_code)]
676    fn count_unique_scopes(
677        &self,
678        allocations: &[crate::export::binary::binary_html_writer::BinaryAllocationData],
679    ) -> u64 {
680        use std::collections::HashSet;
681
682        let unique_scopes: HashSet<&str> = allocations
683            .iter()
684            .map(|alloc| alloc.scope_name.as_str())
685            .collect();
686
687        unique_scopes.len() as u64
688    }
689
690    /// Calculate average scope lifetime
691    #[allow(dead_code)]
692    fn calculate_average_scope_lifetime(
693        &self,
694        allocations: &[crate::export::binary::binary_html_writer::BinaryAllocationData],
695    ) -> f64 {
696        if allocations.is_empty() {
697            return 0.0;
698        }
699
700        let total_lifetime: u64 = allocations
701            .iter()
702            .filter_map(|alloc| alloc.lifetime_ms)
703            .sum();
704
705        let count = allocations
706            .iter()
707            .filter(|alloc| alloc.lifetime_ms.is_some())
708            .count();
709
710        if count == 0 {
711            0.0
712        } else {
713            total_lifetime as f64 / count as f64
714        }
715    }
716
717    /// Calculate memory efficiency metric
718    #[allow(dead_code)]
719    fn calculate_memory_efficiency(&self, data: &BinaryTemplateData) -> f64 {
720        if data.peak_memory_usage == 0 {
721            0.0
722        } else {
723            (data.total_memory_usage as f64 / data.peak_memory_usage as f64) * 100.0
724        }
725    }
726
727    /// Calculate processing speed in MB/s
728    #[allow(dead_code)]
729    fn calculate_processing_speed(&self, data: &BinaryTemplateData) -> f64 {
730        if data.processing_time_ms == 0 {
731            0.0
732        } else {
733            let total_mb = data.total_memory_usage as f64 / (1024.0 * 1024.0);
734            let time_seconds = data.processing_time_ms as f64 / 1000.0;
735            total_mb / time_seconds
736        }
737    }
738
739    /// Ultra-fast optimization for template data - minimal processing
740    fn optimize_template_data_for_size(
741        &self,
742        data: &BinaryTemplateData,
743    ) -> Result<BinaryTemplateData, BinaryExportError> {
744        const MAX_ALLOCATIONS_ULTRA_FAST: usize = 200; // Even smaller for maximum speed
745
746        let mut optimized_data = data.clone();
747
748        // Ultra-fast optimization - aggressive truncation
749        if data.allocations.len() > MAX_ALLOCATIONS_ULTRA_FAST {
750            tracing::info!(
751                "🚀 Ultra-fast optimization: {} → {} allocations",
752                data.allocations.len(),
753                MAX_ALLOCATIONS_ULTRA_FAST
754            );
755
756            // Take first N allocations - no sorting, no filtering to save maximum time
757            optimized_data
758                .allocations
759                .truncate(MAX_ALLOCATIONS_ULTRA_FAST);
760        }
761
762        Ok(optimized_data)
763    }
764
765    /// Load SVG images for embedding in template
766    fn load_svg_images(&self) -> Result<String, BinaryExportError> {
767        let mut svg_data = String::new();
768
769        // List of SVG files to embed
770        let svg_files = [
771            ("memoryAnalysis", "images/memoryAnalysis.svg"),
772            ("lifecycleTimeline", "images/lifecycleTimeline.svg"),
773            ("unsafe_ffi_dashboard", "images/unsafe_ffi_dashboard.svg"),
774        ];
775
776        svg_data.push_str("<script>\n");
777        svg_data.push_str("// Embedded SVG images\n");
778        svg_data.push_str("window.svgImages = {\n");
779
780        for (name, path) in &svg_files {
781            if let Ok(svg_content) = std::fs::read_to_string(path) {
782                // Escape the SVG content for JavaScript
783                let escaped_svg = svg_content
784                    .replace('\\', "\\\\")
785                    .replace('`', "\\`")
786                    .replace("${", "\\${");
787
788                svg_data.push_str(&format!("  {name}: `{escaped_svg}`,\n"));
789            } else {
790                // If SVG file doesn't exist, create a placeholder
791                svg_data.push_str(&format!("  {name}: `<svg width=\"100\" height=\"100\" xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"100\" height=\"100\" fill=\"#f0f0f0\"/><text x=\"50\" y=\"50\" text-anchor=\"middle\" dy=\".3em\" font-family=\"Arial\" font-size=\"12\" fill=\"#666\">SVG Missing</text></svg>`,\n"));
792            }
793        }
794
795        svg_data.push_str("};\n");
796        svg_data.push_str("</script>\n");
797
798        Ok(svg_data)
799    }
800
801    /// Get embedded CSS content
802    fn _get_embedded_css(&self) -> String {
803        r#"
804        /* Binary Dashboard Specific Styles */
805        .binary-performance-indicator {
806            background: linear-gradient(45deg, #3b82f6, #1d4ed8);
807            color: white;
808            padding: 4px 12px;
809            border-radius: 16px;
810            font-size: 0.8rem;
811            font-weight: 600;
812            display: inline-flex;
813            align-items: center;
814            gap: 4px;
815        }
816
817        .binary-stats-grid {
818            display: grid;
819            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
820            gap: 1rem;
821            margin: 1rem 0;
822        }
823
824        .binary-stat-card {
825            background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
826            border: 1px solid #cbd5e0;
827            border-radius: 0.5rem;
828            padding: 1rem;
829            text-align: center;
830            transition: transform 0.2s ease;
831        }
832
833        .binary-stat-card:hover {
834            transform: translateY(-2px);
835            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
836        }
837
838        .binary-processing-badge {
839            background: linear-gradient(45deg, #10b981, #059669);
840            color: white;
841            padding: 2px 8px;
842            border-radius: 12px;
843            font-size: 0.75rem;
844            font-weight: 600;
845            text-transform: uppercase;
846            letter-spacing: 0.5px;
847        }
848
849        /* Dark mode adjustments for binary dashboard */
850        .dark .binary-stat-card {
851            background: linear-gradient(135deg, #374151 0%, #4b5563 100%);
852            border-color: #6b7280;
853        }
854
855        /* Performance indicators */
856        .performance-metric {
857            display: flex;
858            align-items: center;
859            justify-content: space-between;
860            padding: 0.5rem 0;
861            border-bottom: 1px solid #e5e7eb;
862        }
863
864        .performance-metric:last-child {
865            border-bottom: none;
866        }
867
868        .performance-value {
869            font-weight: 600;
870            color: #059669;
871        }
872
873        /* Binary data table enhancements */
874        .binary-table-row:hover {
875            background-color: rgba(59, 130, 246, 0.05);
876        }
877
878        .binary-pointer {
879            font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
880            font-size: 0.875rem;
881            color: #6366f1;
882        }
883
884        /* Responsive adjustments */
885        @media (max-width: 768px) {
886            .binary-stats-grid {
887                grid-template-columns: repeat(2, 1fr);
888                gap: 0.5rem;
889            }
890            
891            .binary-stat-card {
892                padding: 0.75rem;
893            }
894        }
895        "#
896        .to_string()
897    }
898
899    /// Get embedded JavaScript content
900    fn _get_embedded_js(&self) -> String {
901        // Load script.js content if available, otherwise use embedded content
902        let script_js_content =
903            std::fs::read_to_string("templates/script.js").unwrap_or_else(|_| String::new());
904
905        let embedded_js = r#"
906        // Binary Dashboard Specific JavaScript
907        
908        // Performance monitoring
909        function trackBinaryPerformance() {
910            const startTime = performance.now();
911            
912            return {
913                end: function() {
914                    const endTime = performance.now();
915                    return endTime - startTime;
916                }
917            };
918        }
919
920        // Binary data processing utilities
921        function processBinaryData(data) {
922            if (!data || !data.memory_analysis) {
923                console.warn('No binary data available');
924                return null;
925            }
926
927            return {
928                allocations: data.memory_analysis.allocations || [],
929                summary: data.summary || {},
930                performance: data.performance_metrics || {}
931            };
932        }
933
934        // Enhanced table sorting for binary data
935        function sortBinaryTable(column, direction = 'asc') {
936            const table = document.getElementById('allocations-table');
937            if (!table) return;
938
939            const rows = Array.from(table.querySelectorAll('tr')).slice(1); // Skip header
940            
941            rows.sort((a, b) => {
942                const aVal = a.cells[getColumnIndex(column)].textContent.trim();
943                const bVal = b.cells[getColumnIndex(column)].textContent.trim();
944                
945                // Handle different data types
946                if (column === 'size') {
947                    return direction === 'asc' ? 
948                        parseBytes(aVal) - parseBytes(bVal) : 
949                        parseBytes(bVal) - parseBytes(aVal);
950                } else if (column === 'ptr') {
951                    const aPtr = parseInt(aVal.replace('0x', ''), 16);
952                    const bPtr = parseInt(bVal.replace('0x', ''), 16);
953                    return direction === 'asc' ? aPtr - bPtr : bPtr - aPtr;
954                } else {
955                    return direction === 'asc' ? 
956                        aVal.localeCompare(bVal) : 
957                        bVal.localeCompare(aVal);
958                }
959            });
960
961            // Re-append sorted rows
962            rows.forEach(row => table.appendChild(row));
963        }
964
965        function getColumnIndex(column) {
966            const columns = { 'ptr': 0, 'variable': 1, 'type': 2, 'size': 3, 'status': 4 };
967            return columns[column] || 0;
968        }
969
970        function parseBytes(str) {
971            const match = str.match(/^([\d.]+)\s*([KMGT]?B)$/i);
972            if (!match) return 0;
973            
974            const value = parseFloat(match[1]);
975            const unit = match[2].toUpperCase();
976            
977            const multipliers = { 'B': 1, 'KB': 1024, 'MB': 1024*1024, 'GB': 1024*1024*1024 };
978            return value * (multipliers[unit] || 1);
979        }
980
981        // Binary-specific chart configurations
982        function createBinaryCharts() {
983            // Enhanced chart configurations for binary data
984            Chart.defaults.font.family = "'Inter', sans-serif";
985            Chart.defaults.color = '#6b7280';
986            
987            // Add binary-specific chart plugins
988            Chart.register({
989                id: 'binaryDataPlugin',
990                beforeDraw: function(chart) {
991                    if (chart.config.options.plugins?.binaryIndicator) {
992                        const ctx = chart.ctx;
993                        ctx.save();
994                        ctx.fillStyle = '#3b82f6';
995                        ctx.font = '12px Inter';
996                        ctx.fillText('Binary Source', 10, 20);
997                        ctx.restore();
998                    }
999                }
1000            });
1001        }
1002
1003        // Initialize binary dashboard features
1004        function initializeBinaryFeatures() {
1005            // Add binary-specific event listeners
1006            document.addEventListener('keydown', function(e) {
1007                if (e.ctrlKey && e.key === 'b') {
1008                    e.preventDefault();
1009                    showBinaryInfo();
1010                }
1011            });
1012
1013            // Add performance monitoring
1014            const perfMonitor = trackBinaryPerformance();
1015            
1016            // Setup binary data refresh
1017            setInterval(function() {
1018                updateBinaryMetrics();
1019            }, 5000);
1020
1021            console.log('Binary dashboard features initialized');
1022        }
1023
1024        function showBinaryInfo() {
1025            const info = {
1026                dataSource: 'Binary Direct',
1027                processingMode: 'Streaming',
1028                memoryEfficient: true,
1029                performanceOptimized: true
1030            };
1031            
1032            console.table(info);
1033        }
1034
1035        function updateBinaryMetrics() {
1036            // Update real-time metrics if available
1037            if (window.analysisData && window.analysisData.performance_metrics) {
1038                const metrics = window.analysisData.performance_metrics;
1039                
1040                // Update throughput display
1041                const throughputEl = document.getElementById('throughput');
1042                if (throughputEl && metrics.throughput_allocations_per_sec) {
1043                    throughputEl.textContent = Math.round(metrics.throughput_allocations_per_sec).toLocaleString();
1044                }
1045            }
1046        }
1047
1048        // Safe element update function
1049        function safeUpdateElement(id, value) {
1050            const element = document.getElementById(id);
1051            if (element) {
1052                element.textContent = value;
1053            } else {
1054                console.warn('Element not found:', id);
1055            }
1056        }
1057
1058        // Destroy existing charts to prevent canvas reuse errors
1059        function destroyExistingCharts() {
1060            if (window.Chart && window.Chart.getChart) {
1061                const canvasIds = ['memory-distribution-chart', 'allocation-size-chart', 'memory-timeline-chart', 'lifecycle-timeline-chart'];
1062                canvasIds.forEach(canvasId => {
1063                    const existingChart = window.Chart.getChart(canvasId);
1064                    if (existingChart) {
1065                        existingChart.destroy();
1066                        console.log('Destroyed existing chart:', canvasId);
1067                    }
1068                });
1069            }
1070        }
1071
1072        // Wait for all scripts to load, then override functions
1073        window.addEventListener('load', function() {
1074            console.log('All scripts loaded, applying safe overrides...');
1075            
1076            // Destroy any existing charts first
1077            destroyExistingCharts();
1078            
1079            // Override the problematic functions with safe versions
1080            window.updateSummaryStats = function(allocations) {
1081                if (!allocations) return;
1082                
1083                const totalAllocations = allocations.length;
1084                const totalMemory = allocations.reduce((sum, alloc) => sum + (alloc.size || 0), 0);
1085                const activeAllocations = allocations.filter(alloc => alloc.status === 'Active').length;
1086
1087                // Use safe updates for elements that exist
1088                safeUpdateElement('total-allocations', totalAllocations.toLocaleString());
1089                safeUpdateElement('total-memory', formatBytes(totalMemory));
1090                safeUpdateElement('active-allocations', activeAllocations.toLocaleString());
1091                safeUpdateElement('peak-memory', formatBytes(totalMemory));
1092
1093                // Update complex types stats safely
1094                if (window.analysisData && window.analysisData.complex_types) {
1095                    const complexTypes = window.analysisData.complex_types;
1096                    const summary = complexTypes.summary || {};
1097                    safeUpdateElement('system-complex-types', summary.total_complex_types || 0);
1098                    safeUpdateElement('system-smart-pointers', summary.smart_pointers_count || 0);
1099                    safeUpdateElement('system-collections', summary.collections_count || 0);
1100                    safeUpdateElement('system-generic-types', summary.generic_types_count || 0);
1101                }
1102            };
1103
1104            window.populateAllocationsTable = function(allocations) {
1105                const tableBody = document.getElementById('allocations-table');
1106                if (!tableBody) {
1107                    console.warn('Allocations table not found');
1108                    return;
1109                }
1110
1111                if (!allocations || allocations.length === 0) {
1112                    tableBody.innerHTML = '<tr><td colspan="5" class="px-4 py-8 text-center text-gray-500">No allocation data available</td></tr>';
1113                    return;
1114                }
1115
1116                // Update summary statistics safely
1117                window.updateSummaryStats(allocations);
1118
1119                // Populate table with first 100 allocations
1120                const displayAllocations = allocations.slice(0, 100);
1121                tableBody.innerHTML = displayAllocations.map(alloc => `
1122                    <tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
1123                        <td class="px-4 py-3 text-sm font-mono">0x${alloc.id ? alloc.id.toString(16) : 'N/A'}</td>
1124                        <td class="px-4 py-3 text-sm">${alloc.location || alloc.var_name || 'unnamed'}</td>
1125                        <td class="px-4 py-3 text-sm">${alloc.type_name || 'Unknown'}</td>
1126                        <td class="px-4 py-3 text-sm text-right">${formatBytes(alloc.size || 0)}</td>
1127                        <td class="px-4 py-3 text-sm text-right">
1128                            <span class="px-2 py-1 text-xs rounded-full ${alloc.status === 'Active' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}">
1129                                ${alloc.status || 'Unknown'}
1130                            </span>
1131                        </td>
1132                    </tr>
1133                `).join('');
1134            };
1135
1136            // Prevent multiple initializations
1137            if (window.dashboardInitialized) {
1138                console.log('Dashboard already initialized, skipping...');
1139                return;
1140            }
1141
1142            // Initialize user data metrics safely
1143            if (typeof initializeUserDataMetrics === 'function') {
1144                try {
1145                    initializeUserDataMetrics();
1146                } catch (e) {
1147                    console.warn('Error initializing user data metrics:', e);
1148                }
1149            }
1150            
1151            if (typeof initializeLifecycleVisualization === 'function') {
1152                try {
1153                    initializeLifecycleVisualization();
1154                } catch (e) {
1155                    console.warn('Error initializing lifecycle visualization:', e);
1156                }
1157            }
1158            
1159            if (typeof initializeSystemDataMetrics === 'function') {
1160                try {
1161                    initializeSystemDataMetrics();
1162                } catch (e) {
1163                    console.warn('Error initializing system data metrics:', e);
1164                }
1165            }
1166
1167            // Initialize allocations table if data is available
1168            if (window.analysisData && window.analysisData.memory_analysis) {
1169                const allocations = window.analysisData.memory_analysis.allocations || [];
1170                window.populateAllocationsTable(allocations);
1171            }
1172
1173            window.dashboardInitialized = true;
1174            console.log('Safe dashboard initialization complete');
1175        });
1176
1177        // Export binary dashboard utilities
1178        window.binaryDashboard = {
1179            trackPerformance: trackBinaryPerformance,
1180            processData: processBinaryData,
1181            sortTable: sortBinaryTable,
1182            createCharts: createBinaryCharts,
1183            initialize: initializeBinaryFeatures,
1184            safeUpdate: safeUpdateElement,
1185            updateStats: updateSummaryStats,
1186            populateTable: populateAllocationsTable
1187        };
1188        "#;
1189
1190        // Combine script.js content with embedded JS
1191        if !script_js_content.is_empty() {
1192            format!("{script_js_content}\n\n// === EMBEDDED SAFE OVERRIDES ===\n{embedded_js}")
1193        } else {
1194            embedded_js.to_string()
1195        }
1196    }
1197
1198    /// Get performance statistics
1199    pub fn get_stats(&self) -> BinaryTemplateEngineStats {
1200        // Use config field to ensure it's read
1201        let cache_enabled = self.config.enable_cache;
1202        tracing::debug!(
1203            "Getting stats for engine with cache enabled: {}",
1204            cache_enabled
1205        );
1206
1207        BinaryTemplateEngineStats {
1208            templates_processed: self.templates_processed,
1209            last_render_time_ms: self.last_render_time_ms,
1210            cache_hits: self.cache_hits,
1211            cache_hit_rate: if self.templates_processed > 0 {
1212                (self.cache_hits as f64 / self.templates_processed as f64) * 100.0
1213            } else {
1214                0.0
1215            },
1216            cached_templates: 0, // Now handled by resource manager
1217        }
1218    }
1219
1220    /// Get last render time in milliseconds
1221    pub fn last_render_time(&self) -> u64 {
1222        self.last_render_time_ms
1223    }
1224
1225    /// Clear template cache
1226    pub fn clear_cache(&mut self) {
1227        // Clear resource manager cache
1228        self.resource_manager.clear_cache();
1229    }
1230}
1231
1232impl Default for BinaryTemplateEngine {
1233    fn default() -> Self {
1234        Self::new().expect("Failed to create default BinaryTemplateEngine")
1235    }
1236}
1237
1238/// Statistics for binary template engine performance
1239#[derive(Debug, Clone)]
1240pub struct BinaryTemplateEngineStats {
1241    /// Total number of templates processed
1242    pub templates_processed: u64,
1243
1244    /// Last render time in milliseconds
1245    pub last_render_time_ms: u64,
1246
1247    /// Number of cache hits
1248    pub cache_hits: u64,
1249
1250    /// Cache hit rate as percentage
1251    pub cache_hit_rate: f64,
1252
1253    /// Number of cached templates
1254    pub cached_templates: usize,
1255}
1256
1257#[cfg(test)]
1258mod tests {
1259    use super::*;
1260    use crate::export::binary::binary_html_writer::{BinaryAllocationData, BinaryFieldValue};
1261    use std::collections::HashMap;
1262
1263    fn create_test_template_data() -> BinaryTemplateData {
1264        let mut optional_fields = HashMap::new();
1265        optional_fields.insert(
1266            "test_field".to_string(),
1267            BinaryFieldValue::String("test_value".to_string()),
1268        );
1269
1270        let allocation = BinaryAllocationData {
1271            id: 1,
1272            size: 1024,
1273            type_name: "Vec<u8>".to_string(),
1274            scope_name: "main".to_string(),
1275            timestamp_alloc: 1234567890,
1276            is_active: true,
1277            ptr: 0x1000,
1278            thread_id: "main".to_string(),
1279            var_name: Some("test_var".to_string()),
1280            borrow_count: 0,
1281            is_leaked: false,
1282            lifetime_ms: Some(1000),
1283            optional_fields,
1284        };
1285
1286        let allocations = vec![allocation];
1287        BinaryTemplateData {
1288            project_name: "test_project".to_string(),
1289            allocations: allocations.clone(),
1290            total_memory_usage: 1024,
1291            peak_memory_usage: 1024,
1292            active_allocations_count: 1,
1293            processing_time_ms: 100,
1294            data_source: "binary_direct".to_string(),
1295            complex_types: None, // Use proper analyzer instead of JSON functions
1296            unsafe_ffi: None,    // Use proper analyzer instead of JSON functions
1297            variable_relationships: None, // Use proper analyzer instead of JSON functions
1298        }
1299    }
1300
1301    #[test]
1302    fn test_binary_template_engine_creation() {
1303        let engine = BinaryTemplateEngine::new();
1304        assert!(engine.is_ok());
1305    }
1306
1307    #[test]
1308    fn test_template_data_serialization() {
1309        let engine = BinaryTemplateEngine::new().expect("Failed to get test value");
1310        let template_data = create_test_template_data();
1311
1312        let json_result = engine.serialize_template_data(&template_data);
1313        assert!(json_result.is_ok());
1314
1315        let json_str = json_result.expect("Test operation failed");
1316        // The project name is not directly in the JSON, it's used in template processing
1317        // Check for actual content that should be in the serialized data
1318        assert!(json_str.contains("memory_analysis"));
1319        assert!(json_str.contains("Vec<u8>"));
1320        assert!(json_str.contains("allocations"));
1321    }
1322
1323    #[test]
1324    fn test_css_and_js_loading() {
1325        let engine = BinaryTemplateEngine::new().expect("Failed to get test value");
1326
1327        let css_content = engine._get_embedded_css();
1328        let js_content = engine._get_embedded_js();
1329
1330        // Test that CSS and JS content is loaded (content depends on actual files)
1331        assert!(!css_content.is_empty());
1332        assert!(!js_content.is_empty());
1333    }
1334
1335    #[test]
1336    fn test_placeholder_processing() {
1337        let engine = BinaryTemplateEngine::new().expect("Failed to get test value");
1338        let template_data = create_test_template_data();
1339
1340        let template = "Project: {{PROJECT_NAME}}, Time: {{PROCESSING_TIME}}ms";
1341        let json_data = "{}";
1342        let css_content = "";
1343        let js_content = "";
1344
1345        let result = engine._process_template_placeholders(
1346            template,
1347            &template_data,
1348            json_data,
1349            css_content,
1350            js_content,
1351        );
1352
1353        assert!(result.is_ok());
1354        let processed = result.expect("Test operation failed");
1355        assert!(processed.contains("test_project"));
1356        assert!(processed.contains("100ms"));
1357    }
1358
1359    #[test]
1360    fn test_throughput_calculation() {
1361        let engine = BinaryTemplateEngine::new().expect("Failed to get test value");
1362        let template_data = create_test_template_data();
1363
1364        let throughput = engine.calculate_throughput(&template_data);
1365        assert_eq!(throughput, 10.0); // 1 allocation / 100ms * 1000 = 10 allocs/sec
1366    }
1367
1368    #[test]
1369    fn test_caching_functionality() {
1370        let engine = BinaryTemplateEngine::with_config(BinaryTemplateEngineConfig {
1371            enable_cache: true,
1372            ..Default::default()
1373        })
1374        .expect("Test operation failed");
1375
1376        // Note: Resources are now managed by TemplateResourceManager
1377        // Cache functionality is handled internally
1378
1379        // Test that subsequent loads return the same content
1380        let css1 = engine._get_embedded_css();
1381        let css2 = engine._get_embedded_css();
1382        assert_eq!(css1, css2); // Should be identical
1383
1384        let js1 = engine._get_embedded_js();
1385        let js2 = engine._get_embedded_js();
1386        assert_eq!(js1, js2); // Should be identical
1387
1388        // Template caching is now handled by resource manager internally
1389        // No direct access to cache needed
1390
1391        // Verify stats reflect the processing
1392        let stats = engine.get_stats();
1393        assert_eq!(stats.cached_templates, 0); // Now handled by resource manager
1394    }
1395
1396    #[test]
1397    fn test_cache_hits_tracking() {
1398        let engine = BinaryTemplateEngine::with_config(BinaryTemplateEngineConfig {
1399            enable_cache: true,
1400            ..Default::default()
1401        })
1402        .expect("Test operation failed");
1403
1404        // Cache hits are now managed by resource manager internally
1405        // Test that resources can be loaded multiple times without error
1406        engine._get_embedded_css();
1407        engine._get_embedded_js();
1408
1409        // Load again - should work without error
1410        engine._get_embedded_css();
1411        engine._get_embedded_js();
1412
1413        // One more CSS load
1414        engine._get_embedded_css();
1415
1416        // Verify stats are still accessible
1417        let stats = engine.get_stats();
1418        assert_eq!(stats.cache_hits, 0); // Cache hits now managed by resource manager
1419    }
1420
1421    #[test]
1422    fn test_cache_disabled() {
1423        let engine = BinaryTemplateEngine::with_config(BinaryTemplateEngineConfig {
1424            enable_cache: false,
1425            ..Default::default()
1426        })
1427        .expect("Test operation failed");
1428
1429        // With caching disabled, resource manager handles loading differently
1430        // No direct cache access needed
1431        assert_eq!(engine.get_stats().cache_hits, 0);
1432
1433        // Load resources - should not be cached
1434        engine._get_embedded_css();
1435        engine._get_embedded_js();
1436
1437        // Cache is managed internally by resource manager
1438        // No direct verification needed
1439        assert_eq!(engine.get_stats().cache_hits, 0);
1440    }
1441
1442    #[test]
1443    fn test_cache_clearing() {
1444        let mut engine = BinaryTemplateEngine::with_config(BinaryTemplateEngineConfig {
1445            enable_cache: true,
1446            ..Default::default()
1447        })
1448        .expect("Test operation failed");
1449
1450        // Load resources to populate cache
1451        engine._get_embedded_css();
1452        engine._get_embedded_js();
1453
1454        // Cache is managed internally by resource manager
1455        // Test that clear_cache method works without errors
1456        engine.clear_cache();
1457
1458        // Verify engine still functions after cache clear
1459        let test_data = create_test_template_data();
1460        let result = engine.render_binary_template(&test_data);
1461        assert!(result.is_ok());
1462    }
1463
1464    #[test]
1465    fn test_binary_template_engine_config_default() {
1466        let config = BinaryTemplateEngineConfig::default();
1467
1468        assert!(config.enable_cache);
1469        assert!(config.enable_precompilation);
1470        assert!(!config.enable_data_compression);
1471        assert_eq!(config.max_cache_size_mb, 10);
1472        assert_eq!(config.processing_timeout_secs, 30);
1473    }
1474
1475    #[test]
1476    fn test_binary_template_engine_config_debug_clone() {
1477        let config = BinaryTemplateEngineConfig {
1478            enable_cache: false,
1479            enable_precompilation: false,
1480            enable_data_compression: true,
1481            max_cache_size_mb: 20,
1482            processing_timeout_secs: 60,
1483        };
1484
1485        // Test Debug trait
1486        let debug_str = format!("{:?}", config);
1487        assert!(debug_str.contains("BinaryTemplateEngineConfig"));
1488        assert!(debug_str.contains("enable_cache"));
1489        assert!(debug_str.contains("false")); // enable_cache is false
1490
1491        // Test Clone trait
1492        let cloned_config = config.clone();
1493        assert_eq!(cloned_config.enable_cache, config.enable_cache);
1494        assert_eq!(
1495            cloned_config.enable_precompilation,
1496            config.enable_precompilation
1497        );
1498        assert_eq!(
1499            cloned_config.enable_data_compression,
1500            config.enable_data_compression
1501        );
1502        assert_eq!(cloned_config.max_cache_size_mb, config.max_cache_size_mb);
1503        assert_eq!(
1504            cloned_config.processing_timeout_secs,
1505            config.processing_timeout_secs
1506        );
1507    }
1508
1509    #[test]
1510    fn test_binary_template_engine_stats_debug_clone() {
1511        let stats = BinaryTemplateEngineStats {
1512            templates_processed: 10,
1513            last_render_time_ms: 150,
1514            cache_hits: 7,
1515            cache_hit_rate: 70.0,
1516            cached_templates: 5,
1517        };
1518
1519        // Test Debug trait
1520        let debug_str = format!("{:?}", stats);
1521        assert!(debug_str.contains("BinaryTemplateEngineStats"));
1522        assert!(debug_str.contains("templates_processed"));
1523        assert!(debug_str.contains("10"));
1524
1525        // Test Clone trait
1526        let cloned_stats = stats.clone();
1527        assert_eq!(cloned_stats.templates_processed, stats.templates_processed);
1528        assert_eq!(cloned_stats.last_render_time_ms, stats.last_render_time_ms);
1529        assert_eq!(cloned_stats.cache_hits, stats.cache_hits);
1530        assert_eq!(cloned_stats.cache_hit_rate, stats.cache_hit_rate);
1531        assert_eq!(cloned_stats.cached_templates, stats.cached_templates);
1532    }
1533
1534    #[test]
1535    fn test_binary_template_engine_with_custom_config() {
1536        let custom_config = BinaryTemplateEngineConfig {
1537            enable_cache: false,
1538            enable_precompilation: false,
1539            enable_data_compression: true,
1540            max_cache_size_mb: 50,
1541            processing_timeout_secs: 120,
1542        };
1543
1544        let engine = BinaryTemplateEngine::with_config(custom_config.clone());
1545        assert!(engine.is_ok());
1546
1547        let engine = engine.unwrap();
1548        assert_eq!(engine.config.enable_cache, custom_config.enable_cache);
1549        assert_eq!(
1550            engine.config.enable_precompilation,
1551            custom_config.enable_precompilation
1552        );
1553        assert_eq!(
1554            engine.config.enable_data_compression,
1555            custom_config.enable_data_compression
1556        );
1557        assert_eq!(
1558            engine.config.max_cache_size_mb,
1559            custom_config.max_cache_size_mb
1560        );
1561        assert_eq!(
1562            engine.config.processing_timeout_secs,
1563            custom_config.processing_timeout_secs
1564        );
1565    }
1566
1567    #[test]
1568    fn test_binary_template_engine_default_trait() {
1569        let engine1 = BinaryTemplateEngine::new().unwrap();
1570        let engine2 = BinaryTemplateEngine::default();
1571
1572        // Both should have the same configuration
1573        assert_eq!(engine1.config.enable_cache, engine2.config.enable_cache);
1574        assert_eq!(
1575            engine1.config.enable_precompilation,
1576            engine2.config.enable_precompilation
1577        );
1578        assert_eq!(
1579            engine1.config.enable_data_compression,
1580            engine2.config.enable_data_compression
1581        );
1582        assert_eq!(
1583            engine1.config.max_cache_size_mb,
1584            engine2.config.max_cache_size_mb
1585        );
1586        assert_eq!(
1587            engine1.config.processing_timeout_secs,
1588            engine2.config.processing_timeout_secs
1589        );
1590    }
1591
1592    #[test]
1593    fn test_render_binary_template_full_workflow() {
1594        let mut engine = BinaryTemplateEngine::new().unwrap();
1595        let template_data = create_test_template_data();
1596
1597        let result = engine.render_binary_template(&template_data);
1598        assert!(result.is_ok());
1599
1600        let html_content = result.unwrap();
1601        assert!(!html_content.is_empty());
1602
1603        // Verify stats were updated
1604        let stats = engine.get_stats();
1605        assert_eq!(stats.templates_processed, 1);
1606        assert!(stats.last_render_time_ms > 0);
1607    }
1608
1609    #[test]
1610    fn test_render_binary_template_with_large_dataset() {
1611        let mut engine = BinaryTemplateEngine::new().unwrap();
1612
1613        // Create a large dataset to test optimization
1614        let mut large_allocations = Vec::new();
1615        for i in 0..1000 {
1616            let mut optional_fields = HashMap::new();
1617            optional_fields.insert(
1618                "test_field".to_string(),
1619                BinaryFieldValue::String(format!("test_value_{}", i)),
1620            );
1621
1622            large_allocations.push(BinaryAllocationData {
1623                id: i as u64,
1624                size: 1024 + i as usize,
1625                type_name: format!("Type{}", i % 10),
1626                scope_name: format!("scope_{}", i % 5),
1627                timestamp_alloc: 1234567890 + i as u64,
1628                is_active: i % 2 == 0,
1629                ptr: 0x1000 + i as usize,
1630                thread_id: format!("thread_{}", i % 3),
1631                var_name: Some(format!("var_{}", i)),
1632                borrow_count: (i % 5) as usize,
1633                is_leaked: i % 10 == 0,
1634                lifetime_ms: Some(1000 + i as u64),
1635                optional_fields,
1636            });
1637        }
1638
1639        let large_template_data = BinaryTemplateData {
1640            project_name: "large_test_project".to_string(),
1641            allocations: large_allocations,
1642            total_memory_usage: 1024000,
1643            peak_memory_usage: 1024000,
1644            active_allocations_count: 500,
1645            processing_time_ms: 1000,
1646            data_source: "binary_direct".to_string(),
1647            complex_types: None,
1648            unsafe_ffi: None,
1649            variable_relationships: None,
1650        };
1651
1652        let result = engine.render_binary_template(&large_template_data);
1653        assert!(result.is_ok());
1654
1655        let html_content = result.unwrap();
1656        assert!(!html_content.is_empty());
1657
1658        // Verify the template was processed successfully
1659        // The project name might be embedded in different ways in the template
1660        assert!(!html_content.is_empty());
1661
1662        // Verify that the HTML contains some expected structure
1663        assert!(
1664            html_content.contains("html")
1665                || html_content.contains("HTML")
1666                || html_content.len() > 1000
1667        ); // Should be a substantial HTML document
1668    }
1669
1670    #[test]
1671    fn test_optimize_template_data_for_size() {
1672        let engine = BinaryTemplateEngine::new().unwrap();
1673
1674        // Create data with more than 200 allocations
1675        let mut many_allocations = Vec::new();
1676        for i in 0..500 {
1677            many_allocations.push(BinaryAllocationData {
1678                id: i as u64,
1679                size: 1024,
1680                type_name: "TestType".to_string(),
1681                scope_name: "test_scope".to_string(),
1682                timestamp_alloc: 1234567890,
1683                is_active: true,
1684                ptr: 0x1000 + i as usize,
1685                thread_id: "main".to_string(),
1686                var_name: Some(format!("var_{}", i)),
1687                borrow_count: 0,
1688                is_leaked: false,
1689                lifetime_ms: Some(1000),
1690                optional_fields: HashMap::new(),
1691            });
1692        }
1693
1694        let large_data = BinaryTemplateData {
1695            project_name: "test_project".to_string(),
1696            allocations: many_allocations,
1697            total_memory_usage: 512000,
1698            peak_memory_usage: 512000,
1699            active_allocations_count: 500,
1700            processing_time_ms: 100,
1701            data_source: "binary_direct".to_string(),
1702            complex_types: None,
1703            unsafe_ffi: None,
1704            variable_relationships: None,
1705        };
1706
1707        let result = engine.optimize_template_data_for_size(&large_data);
1708        assert!(result.is_ok());
1709
1710        let optimized_data = result.unwrap();
1711        assert_eq!(optimized_data.allocations.len(), 200); // Should be truncated to MAX_ALLOCATIONS_ULTRA_FAST
1712        assert_eq!(optimized_data.project_name, large_data.project_name);
1713        assert_eq!(
1714            optimized_data.total_memory_usage,
1715            large_data.total_memory_usage
1716        );
1717    }
1718
1719    #[test]
1720    fn test_generate_fast_timeline_data() {
1721        let engine = BinaryTemplateEngine::new().unwrap();
1722
1723        // Test with empty allocations
1724        let empty_allocations = vec![];
1725        let timeline = engine.generate_fast_timeline_data(&empty_allocations);
1726        assert!(timeline.is_empty());
1727
1728        // Test with some allocations
1729        let allocations = vec![
1730            BinaryAllocationData {
1731                id: 1,
1732                size: 1000,
1733                type_name: "Type1".to_string(),
1734                scope_name: "scope1".to_string(),
1735                timestamp_alloc: 1234567890,
1736                is_active: true,
1737                ptr: 0x1000,
1738                thread_id: "main".to_string(),
1739                var_name: Some("var1".to_string()),
1740                borrow_count: 0,
1741                is_leaked: false,
1742                lifetime_ms: Some(1000),
1743                optional_fields: HashMap::new(),
1744            },
1745            BinaryAllocationData {
1746                id: 2,
1747                size: 2000,
1748                type_name: "Type2".to_string(),
1749                scope_name: "scope2".to_string(),
1750                timestamp_alloc: 1234567900,
1751                is_active: true,
1752                ptr: 0x2000,
1753                thread_id: "main".to_string(),
1754                var_name: Some("var2".to_string()),
1755                borrow_count: 0,
1756                is_leaked: false,
1757                lifetime_ms: Some(2000),
1758                optional_fields: HashMap::new(),
1759            },
1760        ];
1761
1762        let timeline = engine.generate_fast_timeline_data(&allocations);
1763        assert_eq!(timeline.len(), 5); // Should generate 5 data points
1764
1765        // Verify timeline structure
1766        assert!(timeline[0]["timestamp"].as_u64().unwrap() == 0);
1767        assert!(timeline[4]["timestamp"].as_u64().unwrap() == 1000000);
1768        assert!(timeline[4]["memory_usage"].as_u64().unwrap() == 3000); // 1000 + 2000
1769        assert!(timeline[4]["allocation_count"].as_u64().unwrap() == 2);
1770    }
1771
1772    #[test]
1773    fn test_generate_fast_size_distribution() {
1774        let engine = BinaryTemplateEngine::new().unwrap();
1775
1776        // Test with empty allocations
1777        let empty_allocations = vec![];
1778        let distribution = engine.generate_fast_size_distribution(&empty_allocations);
1779        assert!(distribution.is_empty());
1780
1781        // Test with various sized allocations
1782        let allocations = vec![
1783            BinaryAllocationData {
1784                id: 1,
1785                size: 512, // Small (0-1KB)
1786                type_name: "SmallType".to_string(),
1787                scope_name: "scope1".to_string(),
1788                timestamp_alloc: 1234567890,
1789                is_active: true,
1790                ptr: 0x1000,
1791                thread_id: "main".to_string(),
1792                var_name: Some("small_var".to_string()),
1793                borrow_count: 0,
1794                is_leaked: false,
1795                lifetime_ms: Some(1000),
1796                optional_fields: HashMap::new(),
1797            },
1798            BinaryAllocationData {
1799                id: 2,
1800                size: 50000, // Medium (1-100KB)
1801                type_name: "MediumType".to_string(),
1802                scope_name: "scope2".to_string(),
1803                timestamp_alloc: 1234567900,
1804                is_active: true,
1805                ptr: 0x2000,
1806                thread_id: "main".to_string(),
1807                var_name: Some("medium_var".to_string()),
1808                borrow_count: 0,
1809                is_leaked: false,
1810                lifetime_ms: Some(2000),
1811                optional_fields: HashMap::new(),
1812            },
1813            BinaryAllocationData {
1814                id: 3,
1815                size: 500000, // Large (100KB-1MB)
1816                type_name: "LargeType".to_string(),
1817                scope_name: "scope3".to_string(),
1818                timestamp_alloc: 1234567910,
1819                is_active: true,
1820                ptr: 0x3000,
1821                thread_id: "main".to_string(),
1822                var_name: Some("large_var".to_string()),
1823                borrow_count: 0,
1824                is_leaked: false,
1825                lifetime_ms: Some(3000),
1826                optional_fields: HashMap::new(),
1827            },
1828            BinaryAllocationData {
1829                id: 4,
1830                size: 2000000, // Huge (>1MB)
1831                type_name: "HugeType".to_string(),
1832                scope_name: "scope4".to_string(),
1833                timestamp_alloc: 1234567920,
1834                is_active: true,
1835                ptr: 0x4000,
1836                thread_id: "main".to_string(),
1837                var_name: Some("huge_var".to_string()),
1838                borrow_count: 0,
1839                is_leaked: false,
1840                lifetime_ms: Some(4000),
1841                optional_fields: HashMap::new(),
1842            },
1843        ];
1844
1845        let distribution = engine.generate_fast_size_distribution(&allocations);
1846        assert_eq!(distribution.len(), 4); // Should have 4 size ranges
1847
1848        // Verify distribution structure
1849        let size_ranges: Vec<&str> = distribution
1850            .iter()
1851            .map(|item| item["size_range"].as_str().unwrap())
1852            .collect();
1853        assert!(size_ranges.contains(&"0-1KB"));
1854        assert!(size_ranges.contains(&"1-100KB"));
1855        assert!(size_ranges.contains(&"100KB-1MB"));
1856        assert!(size_ranges.contains(&">1MB"));
1857    }
1858
1859    #[test]
1860    fn test_generate_fast_lifecycle_events() {
1861        let engine = BinaryTemplateEngine::new().unwrap();
1862
1863        // Create many allocations to test step_by sampling
1864        let mut many_allocations = Vec::new();
1865        for i in 0..1000 {
1866            many_allocations.push(BinaryAllocationData {
1867                id: i as u64,
1868                size: 1024,
1869                type_name: "TestType".to_string(),
1870                scope_name: "test_scope".to_string(),
1871                timestamp_alloc: 1234567890 + i as u64,
1872                is_active: i % 2 == 0,
1873                ptr: 0x1000 + i as usize,
1874                thread_id: "main".to_string(),
1875                var_name: Some(format!("var_{}", i)),
1876                borrow_count: 0,
1877                is_leaked: false,
1878                lifetime_ms: Some(1000),
1879                optional_fields: HashMap::new(),
1880            });
1881        }
1882
1883        let lifecycle_events = engine.generate_fast_lifecycle_events(&many_allocations);
1884        assert!(lifecycle_events.len() <= 20); // Should be limited to 20 events
1885
1886        // Verify event structure
1887        if !lifecycle_events.is_empty() {
1888            let first_event = &lifecycle_events[0];
1889            assert!(first_event.get("id").is_some());
1890            assert!(first_event.get("event_type").is_some());
1891            assert!(first_event.get("timestamp").is_some());
1892            assert!(first_event.get("size").is_some());
1893
1894            let event_type = first_event["event_type"].as_str().unwrap();
1895            assert!(event_type == "Allocation" || event_type == "Deallocation");
1896        }
1897    }
1898
1899    #[test]
1900    fn test_count_unique_scopes() {
1901        let engine = BinaryTemplateEngine::new().unwrap();
1902
1903        let allocations = vec![
1904            BinaryAllocationData {
1905                id: 1,
1906                size: 1024,
1907                type_name: "Type1".to_string(),
1908                scope_name: "scope1".to_string(),
1909                timestamp_alloc: 1234567890,
1910                is_active: true,
1911                ptr: 0x1000,
1912                thread_id: "main".to_string(),
1913                var_name: Some("var1".to_string()),
1914                borrow_count: 0,
1915                is_leaked: false,
1916                lifetime_ms: Some(1000),
1917                optional_fields: HashMap::new(),
1918            },
1919            BinaryAllocationData {
1920                id: 2,
1921                size: 2048,
1922                type_name: "Type2".to_string(),
1923                scope_name: "scope1".to_string(), // Same scope
1924                timestamp_alloc: 1234567900,
1925                is_active: true,
1926                ptr: 0x2000,
1927                thread_id: "main".to_string(),
1928                var_name: Some("var2".to_string()),
1929                borrow_count: 0,
1930                is_leaked: false,
1931                lifetime_ms: Some(2000),
1932                optional_fields: HashMap::new(),
1933            },
1934            BinaryAllocationData {
1935                id: 3,
1936                size: 4096,
1937                type_name: "Type3".to_string(),
1938                scope_name: "scope2".to_string(), // Different scope
1939                timestamp_alloc: 1234567910,
1940                is_active: true,
1941                ptr: 0x3000,
1942                thread_id: "main".to_string(),
1943                var_name: Some("var3".to_string()),
1944                borrow_count: 0,
1945                is_leaked: false,
1946                lifetime_ms: Some(3000),
1947                optional_fields: HashMap::new(),
1948            },
1949        ];
1950
1951        let unique_scopes = engine.count_unique_scopes(&allocations);
1952        assert_eq!(unique_scopes, 2); // scope1 and scope2
1953    }
1954
1955    #[test]
1956    fn test_calculate_average_scope_lifetime() {
1957        let engine = BinaryTemplateEngine::new().unwrap();
1958
1959        // Test with empty allocations
1960        let empty_allocations = vec![];
1961        let avg_lifetime = engine.calculate_average_scope_lifetime(&empty_allocations);
1962        assert_eq!(avg_lifetime, 0.0);
1963
1964        // Test with allocations having lifetime_ms
1965        let allocations = vec![
1966            BinaryAllocationData {
1967                id: 1,
1968                size: 1024,
1969                type_name: "Type1".to_string(),
1970                scope_name: "scope1".to_string(),
1971                timestamp_alloc: 1234567890,
1972                is_active: true,
1973                ptr: 0x1000,
1974                thread_id: "main".to_string(),
1975                var_name: Some("var1".to_string()),
1976                borrow_count: 0,
1977                is_leaked: false,
1978                lifetime_ms: Some(1000),
1979                optional_fields: HashMap::new(),
1980            },
1981            BinaryAllocationData {
1982                id: 2,
1983                size: 2048,
1984                type_name: "Type2".to_string(),
1985                scope_name: "scope2".to_string(),
1986                timestamp_alloc: 1234567900,
1987                is_active: true,
1988                ptr: 0x2000,
1989                thread_id: "main".to_string(),
1990                var_name: Some("var2".to_string()),
1991                borrow_count: 0,
1992                is_leaked: false,
1993                lifetime_ms: Some(2000),
1994                optional_fields: HashMap::new(),
1995            },
1996            BinaryAllocationData {
1997                id: 3,
1998                size: 4096,
1999                type_name: "Type3".to_string(),
2000                scope_name: "scope3".to_string(),
2001                timestamp_alloc: 1234567910,
2002                is_active: true,
2003                ptr: 0x3000,
2004                thread_id: "main".to_string(),
2005                var_name: Some("var3".to_string()),
2006                borrow_count: 0,
2007                is_leaked: false,
2008                lifetime_ms: None, // No lifetime
2009                optional_fields: HashMap::new(),
2010            },
2011        ];
2012
2013        let avg_lifetime = engine.calculate_average_scope_lifetime(&allocations);
2014        assert_eq!(avg_lifetime, 1500.0); // (1000 + 2000) / 2
2015    }
2016
2017    #[test]
2018    fn test_calculate_memory_efficiency() {
2019        let engine = BinaryTemplateEngine::new().unwrap();
2020
2021        // Test with zero peak memory
2022        let zero_peak_data = BinaryTemplateData {
2023            project_name: "test".to_string(),
2024            allocations: vec![],
2025            total_memory_usage: 1000,
2026            peak_memory_usage: 0,
2027            active_allocations_count: 0,
2028            processing_time_ms: 100,
2029            data_source: "binary_direct".to_string(),
2030            complex_types: None,
2031            unsafe_ffi: None,
2032            variable_relationships: None,
2033        };
2034
2035        let efficiency = engine.calculate_memory_efficiency(&zero_peak_data);
2036        assert_eq!(efficiency, 0.0);
2037
2038        // Test with normal data
2039        let normal_data = BinaryTemplateData {
2040            project_name: "test".to_string(),
2041            allocations: vec![],
2042            total_memory_usage: 800,
2043            peak_memory_usage: 1000,
2044            active_allocations_count: 0,
2045            processing_time_ms: 100,
2046            data_source: "binary_direct".to_string(),
2047            complex_types: None,
2048            unsafe_ffi: None,
2049            variable_relationships: None,
2050        };
2051
2052        let efficiency = engine.calculate_memory_efficiency(&normal_data);
2053        assert_eq!(efficiency, 80.0); // (800 / 1000) * 100
2054    }
2055
2056    #[test]
2057    fn test_calculate_processing_speed() {
2058        let engine = BinaryTemplateEngine::new().unwrap();
2059
2060        // Test with zero processing time
2061        let zero_time_data = BinaryTemplateData {
2062            project_name: "test".to_string(),
2063            allocations: vec![],
2064            total_memory_usage: 1024 * 1024, // 1MB
2065            peak_memory_usage: 1024 * 1024,
2066            active_allocations_count: 0,
2067            processing_time_ms: 0,
2068            data_source: "binary_direct".to_string(),
2069            complex_types: None,
2070            unsafe_ffi: None,
2071            variable_relationships: None,
2072        };
2073
2074        let speed = engine.calculate_processing_speed(&zero_time_data);
2075        assert_eq!(speed, 0.0);
2076
2077        // Test with normal data
2078        let normal_data = BinaryTemplateData {
2079            project_name: "test".to_string(),
2080            allocations: vec![],
2081            total_memory_usage: 2 * 1024 * 1024, // 2MB
2082            peak_memory_usage: 2 * 1024 * 1024,
2083            active_allocations_count: 0,
2084            processing_time_ms: 1000, // 1 second
2085            data_source: "binary_direct".to_string(),
2086            complex_types: None,
2087            unsafe_ffi: None,
2088            variable_relationships: None,
2089        };
2090
2091        let speed = engine.calculate_processing_speed(&normal_data);
2092        assert_eq!(speed, 2.0); // 2MB / 1s = 2 MB/s
2093    }
2094
2095    #[test]
2096    fn test_load_svg_images() {
2097        let engine = BinaryTemplateEngine::new().unwrap();
2098
2099        let svg_result = engine.load_svg_images();
2100        assert!(svg_result.is_ok());
2101
2102        let svg_content = svg_result.unwrap();
2103        assert!(!svg_content.is_empty());
2104        assert!(svg_content.contains("window.svgImages"));
2105        assert!(svg_content.contains("memoryAnalysis"));
2106        assert!(svg_content.contains("lifecycleTimeline"));
2107        assert!(svg_content.contains("unsafe_ffi_dashboard"));
2108    }
2109
2110    #[test]
2111    fn test_get_stats_and_last_render_time() {
2112        let mut engine = BinaryTemplateEngine::new().unwrap();
2113
2114        // Initial stats
2115        let initial_stats = engine.get_stats();
2116        assert_eq!(initial_stats.templates_processed, 0);
2117        assert_eq!(initial_stats.last_render_time_ms, 0);
2118        assert_eq!(initial_stats.cache_hits, 0);
2119        assert_eq!(initial_stats.cache_hit_rate, 0.0);
2120        assert_eq!(initial_stats.cached_templates, 0);
2121
2122        // Initial render time
2123        assert_eq!(engine.last_render_time(), 0);
2124
2125        // Process a template
2126        let template_data = create_test_template_data();
2127        let result = engine.render_binary_template(&template_data);
2128        assert!(result.is_ok());
2129
2130        // Updated stats
2131        let updated_stats = engine.get_stats();
2132        assert_eq!(updated_stats.templates_processed, 1);
2133        assert!(updated_stats.last_render_time_ms > 0);
2134        assert!(engine.last_render_time() > 0);
2135        assert_eq!(engine.last_render_time(), updated_stats.last_render_time_ms);
2136    }
2137
2138    #[test]
2139    fn test_throughput_calculation_edge_cases() {
2140        let engine = BinaryTemplateEngine::new().unwrap();
2141
2142        // Test with zero processing time
2143        let zero_time_data = BinaryTemplateData {
2144            project_name: "test".to_string(),
2145            allocations: vec![create_test_template_data().allocations[0].clone()],
2146            total_memory_usage: 1024,
2147            peak_memory_usage: 1024,
2148            active_allocations_count: 1,
2149            processing_time_ms: 0,
2150            data_source: "binary_direct".to_string(),
2151            complex_types: None,
2152            unsafe_ffi: None,
2153            variable_relationships: None,
2154        };
2155
2156        let throughput = engine.calculate_throughput(&zero_time_data);
2157        assert_eq!(throughput, 0.0);
2158
2159        // Test with normal data
2160        let normal_data = BinaryTemplateData {
2161            project_name: "test".to_string(),
2162            allocations: vec![
2163                create_test_template_data().allocations[0].clone(),
2164                create_test_template_data().allocations[0].clone(),
2165            ],
2166            total_memory_usage: 2048,
2167            peak_memory_usage: 2048,
2168            active_allocations_count: 2,
2169            processing_time_ms: 500,
2170            data_source: "binary_direct".to_string(),
2171            complex_types: None,
2172            unsafe_ffi: None,
2173            variable_relationships: None,
2174        };
2175
2176        let throughput = engine.calculate_throughput(&normal_data);
2177        assert_eq!(throughput, 4.0); // 2 allocations / 500ms * 1000 = 4 allocs/sec
2178    }
2179}