jbuild 0.1.9

High-performance Java build tool supporting Maven and Gradle
Documentation
//! Unit tests for interactive functionality
//!
//! These tests focus on testing individual components of the interactive system

use jbuild::interactive::*;
use chrono::Local;

#[cfg(test)]
mod tests {
    use super::*;
    use std::time::Duration;
    use std::sync::mpsc;
    
    // Test the BuildMetrics structure thoroughly
    #[test]
    fn test_build_metrics_lifecycle() {
        let metrics = BuildMetrics::new();
        
        // Test initial state
        assert!(metrics.elapsed().as_millis() >= 0);
        assert!(metrics.phase_times.is_empty());
        assert!(metrics.current_phase.is_none());
        assert_eq!(metrics.processed_dependencies, 0);
        assert_eq!(metrics.total_dependencies, 0);
        assert!(metrics.memory_usage_mb >= 0);
        assert!(metrics.cpu_usage_percent >= 0.0);
    }
    
    #[test]
    fn test_build_metrics_progress_tracking() {
        let mut metrics = BuildMetrics::new();
        
        // Test progress tracking
        metrics.update_progress(10, 100);
        assert_eq!(metrics.processed_dependencies, 10);
        assert_eq!(metrics.total_dependencies, 100);
        
        metrics.update_progress(50, 200);
        assert_eq!(metrics.processed_dependencies, 50);
        assert_eq!(metrics.total_dependencies, 200);
    }
    
    #[test]
    fn test_build_metrics_phase_timing() {
        let mut metrics = BuildMetrics::new();
        
        // Add test phases
        metrics.add_phase_time("Phase 1".to_string(), Duration::from_millis(1000));
        metrics.add_phase_time("Phase 2".to_string(), Duration::from_millis(2000));
        
        assert_eq!(metrics.phase_times.len(), 2);
        assert_eq!(metrics.phase_times[0].0, "Phase 1");
        assert_eq!(metrics.phase_times[1].0, "Phase 2");
        assert_eq!(metrics.phase_times[0].1, Duration::from_millis(1000));
        assert_eq!(metrics.phase_times[1].1, Duration::from_millis(2000));
    }
    
    #[test]
    fn test_build_metrics_memory_tracking() {
        let mut metrics = BuildMetrics::new();
        
        let initial_memory = metrics.memory_usage_mb;
        
        // Simulate memory increase
        metrics.processed_dependencies = 50;
        metrics.memory_usage_mb = 512 + (metrics.processed_dependencies as f64 * 0.1) as u64;
        
        assert!(metrics.memory_usage_mb > initial_memory);
    }
    
    #[test]
    fn test_build_metrics_cpu_tracking() {
        let mut metrics = BuildMetrics::new();
        
        let initial_cpu = metrics.cpu_usage_percent;
        
        // Simulate CPU increase
        metrics.processed_dependencies = 30;
        metrics.cpu_usage_percent = (25.0 + (metrics.processed_dependencies as f64 * 0.5)) as f32;
        
        assert!(metrics.cpu_usage_percent > initial_cpu);
        assert!(metrics.cpu_usage_percent <= 100.0);
    }
    
    // Test profile report generation
    #[test]
    fn test_profile_report_creation() {
        let report = ProfileReport {
            build_time: Duration::from_secs(10),
            success: true,
            error_count: 0,
            phases: vec![
                ("Dependencies".to_string(), Duration::from_millis(2000)),
                ("Compilation".to_string(), Duration::from_millis(6000)),
                ("Testing".to_string(), Duration::from_millis(2000)),
            ],
            timestamp: chrono::Local::now(),
        };
        
        assert_eq!(report.build_time, Duration::from_secs(10));
        assert!(report.success);
        assert_eq!(report.error_count, 0);
        assert_eq!(report.phases.len(), 3);
        assert_eq!(report.phases[0].0, "Dependencies");
    }
    
    // Test progress simulation
    #[test]
    fn test_progress_simulation_structure() {
        let (tx, rx) = mpsc::channel();
        
        // Test that we can send and receive progress updates
        tx.send("Test update".to_string()).unwrap();
        
        let received = rx.recv().unwrap();
        assert_eq!(received, "Test update");
    }
    
    // Test report generation
    #[test]
    fn test_markdown_report_generation() {
        let report = ProfileReport {
            build_time: Duration::from_secs(5),
            success: true,
            error_count: 1,
            phases: vec![
                ("Test Phase".to_string(), Duration::from_millis(2000)),
            ],
            timestamp: chrono::Local::now(),
        };
        
        let markdown = generate_markdown_report(&report);
        
        assert!(markdown.contains("# Build Profile Report"));
        assert!(markdown.contains("**Build Time**"));
        assert!(markdown.contains("✅ Success"));
        assert!(markdown.contains("Test Phase"));
        assert!(markdown.contains("40.0%"));
    }
    
    #[test]
    fn test_text_report_generation() {
        let report = ProfileReport {
            build_time: Duration::from_secs(8),
            success: false,
            error_count: 3,
            phases: vec![
                ("Phase 1".to_string(), Duration::from_millis(3000)),
                ("Phase 2".to_string(), Duration::from_millis(5000)),
            ],
            timestamp: chrono::Local::now(),
        };
        
        let text = generate_text_report(&report);
        
        assert!(text.contains("Build Profile Report"));
        assert!(text.contains("Build Time: 8.00s"));
        assert!(text.contains("FAILED"));
        assert!(text.contains("Phase 1: 3.00s (37.5%)"));
        assert!(text.contains("Phase 2: 5.00s (62.5%)"));
    }
    
    // Test error handling
    #[test]
    fn test_error_handling_for_invalid_formats() {
        let result = generate_profile_report(
            Duration::from_secs(5),
            &None,
            "invalid_format"
        );
        
        // Should handle invalid format gracefully
        match result {
            Ok(_) => {
                // In a real implementation, this might print an error but continue
                println!("Invalid format handled gracefully");
            }
            Err(e) => {
                println!("Error handling invalid format: {}", e);
            }
        }
    }
    
    // Test dashboard display
    #[test]
    fn test_dashboard_display_basic() {
        let metrics = BuildMetrics::new();
        let start_time = std::time::Instant::now();
        
        // Test that dashboard display doesn't panic
        let result = display_dashboard(&metrics, &start_time, jbuild::build::BuildSystem::Maven);
        assert!(result.is_ok());
    }
    
    // Test monitoring functionality
    #[test]
    fn test_monitor_functionality() {
        use std::thread;
        use std::time::Instant;
        
        let metrics = BuildMetrics::new();
        let start_time = Instant::now();
        
        // Test that monitor display works
        let result = display_monitor(&metrics, &start_time, false);
        assert!(result.is_ok());
    }
    
    // Test performance metrics calculations
    #[test]
    fn test_performance_calculations() {
        let mut metrics = BuildMetrics::new();
        
        // Test throughput calculation
        let start_time = std::time::Instant::now();
        std::thread::sleep(Duration::from_millis(100));
        
        metrics.processed_dependencies = 5;
        let elapsed = start_time.elapsed();
        let throughput = metrics.processed_dependencies as f64 / elapsed.as_secs_f64();
        
        assert!(throughput > 0.0);
    }
    
    // Test concurrent access simulation
    #[test]
    fn test_concurrent_metrics_access() {
        let mut handles = vec![];
        
        // Spawn multiple threads to update metrics
        for _i in 0..3 {
            let handle = std::thread::spawn(move || {
                let mut metrics = BuildMetrics::new();
                for j in 0..10 {
                    metrics.processed_dependencies = j;
                    metrics.memory_usage_mb = 512 + (j as f64 * 0.1) as u64;
                }
                metrics
            });
            handles.push(handle);
        }
        
        // Wait for all threads
        let mut total_processed = 0;
        for handle in handles {
            let metrics = handle.join().unwrap();
            total_processed += metrics.processed_dependencies;
        }
        
        // Verify metrics were updated
        assert!(total_processed > 0);
    }
    
    // Test phase timing accuracy
    #[test]
    fn test_phase_timing_accuracy() {
        let mut metrics = BuildMetrics::new();
        
        let phase_start = std::time::Instant::now();
        std::thread::sleep(Duration::from_millis(50));
        let actual_duration = phase_start.elapsed();
        
        metrics.add_phase_time("Test Phase".to_string(), actual_duration);
        
        let recorded_duration = metrics.phase_times[0].1;
        // Allow some tolerance for timing precision
        assert!((recorded_duration.as_millis() as i64 - actual_duration.as_millis() as i64).abs() < 10);
    }
    
    // Test memory usage bounds
    #[test]
    fn test_memory_usage_bounds() {
        let mut metrics = BuildMetrics::new();
        
        // Test with different dependency counts
        for deps in [0, 10, 50, 100, 500] {
            metrics.processed_dependencies = deps;
            metrics.memory_usage_mb = 512 + (deps as f64 * 0.1) as u64;
            
            // Memory should be positive and reasonable
            assert!(metrics.memory_usage_mb > 0);
            assert!(metrics.memory_usage_mb < 10000); // Less than 10GB
        }
    }
    
    // Test CPU usage bounds
    #[test]
    fn test_cpu_usage_bounds() {
        let mut metrics = BuildMetrics::new();
        
        // Test with different dependency counts
        for deps in [0, 10, 50, 100, 500] {
            metrics.processed_dependencies = deps;
            metrics.cpu_usage_percent = (25.0 + (deps as f64 * 0.1)).min(95.0) as f32;
            
            // CPU should be between 0 and 100
            assert!(metrics.cpu_usage_percent >= 0.0);
            assert!(metrics.cpu_usage_percent <= 100.0);
        }
    }
}