use std::env;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time::{Duration, Instant};
use kopi::indicator::factory::ProgressFactory;
use kopi::indicator::status::StatusReporter;
use kopi::indicator::{ProgressConfig, ProgressIndicator, ProgressStyle, SilentProgress};
use serial_test::serial;
mod common;
use common::progress_capture::TestProgressCapture;
use common::test_home::TestHomeGuard;
#[test]
fn test_progress_factory_terminal_detection() {
let mut progress = ProgressFactory::create(true);
let config = ProgressConfig::new(ProgressStyle::Count).with_total(100);
progress.start(config);
progress.update(50, None);
progress.complete(None);
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Count).with_total(100);
progress.start(config);
progress.update(50, None);
progress.complete(None);
}
#[test]
fn test_progress_indicator_with_install_simulation() {
let test_home = TestHomeGuard::new();
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Bytes).with_total(150_000_000);
progress.start(config);
for i in 0..10 {
progress.update(i as u64 * 15_000_000, None);
thread::sleep(Duration::from_millis(10));
}
progress.complete(Some("Installation complete".to_string()));
assert!(test_home.path().exists());
}
#[test]
fn test_progress_indicator_with_cache_operations() {
let _test_home = TestHomeGuard::new();
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Count);
progress.start(config);
for i in 0..5 {
progress.set_message(format!("Processing item {}/5", i + 1));
thread::sleep(Duration::from_millis(10));
}
progress.set_message("Processing distributions...".to_string());
thread::sleep(Duration::from_millis(10));
progress.complete(None);
}
#[test]
fn test_progress_indicator_batch_operations() {
let _test_home = TestHomeGuard::new();
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Count).with_total(5);
progress.start(config);
for i in 0..5 {
progress.set_message(format!("Removing JDK {}/5", i + 1));
progress.update(i as u64 + 1, None);
thread::sleep(Duration::from_millis(10));
}
progress.complete(Some("All JDKs uninstalled".to_string()));
}
#[test]
fn test_no_progress_mode_across_commands() {
let _test_home = TestHomeGuard::new();
let mut progress = ProgressFactory::create(true);
let config = ProgressConfig::new(ProgressStyle::Bytes).with_total(100);
progress.start(config);
progress.update(50, None);
progress.set_message("This should not be visible".to_string());
progress.complete(None);
}
#[test]
#[serial]
fn test_progress_in_ci_environment() {
let original_ci = env::var("CI").ok();
unsafe {
env::set_var("CI", "true");
}
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Count).with_total(100);
progress.start(config);
progress.update(100, None);
progress.complete(None);
unsafe {
match original_ci {
Some(val) => env::set_var("CI", val),
None => env::remove_var("CI"),
}
}
}
#[test]
#[serial]
fn test_progress_with_dumb_terminal() {
let original_term = env::var("TERM").ok();
unsafe {
env::set_var("TERM", "dumb");
}
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Bytes).with_total(50);
progress.start(config);
progress.update(25, None);
progress.complete(None);
unsafe {
match original_term {
Some(val) => env::set_var("TERM", val),
None => env::remove_var("TERM"),
}
}
}
#[test]
fn test_progress_indicator_error_handling() {
let _test_home = TestHomeGuard::new();
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Count).with_total(100);
progress.start(config);
progress.update(50, None);
progress.error("Simulated error occurred".to_string());
progress.complete(None);
}
#[test]
fn test_progress_indicator_concurrent_operations() {
let _test_home = TestHomeGuard::new();
let finished = Arc::new(AtomicBool::new(false));
let mut handles = vec![];
for _i in 0..3 {
let finished = Arc::clone(&finished);
let handle = thread::spawn(move || {
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Count).with_total(50);
progress.start(config);
for j in 0..50 {
if finished.load(Ordering::Relaxed) {
break;
}
progress.update(j, None);
thread::sleep(Duration::from_millis(5));
}
progress.complete(None);
});
handles.push(handle);
}
thread::sleep(Duration::from_millis(100));
finished.store(true, Ordering::Relaxed);
for handle in handles {
handle.join().expect("Thread should complete");
}
}
#[test]
fn test_status_reporter_silent_mode() {
let _test_home = TestHomeGuard::new();
let reporter = StatusReporter::new(true);
reporter.operation("Silent operation", "test context");
reporter.step("Silent step");
reporter.success("Silent success");
reporter.error("This error should be visible");
}
#[test]
fn test_status_reporter_normal_mode() {
let _test_home = TestHomeGuard::new();
let reporter = StatusReporter::new(false);
reporter.operation("Starting operation", "test context");
reporter.step("Step 1: Preparing");
reporter.step("Step 2: Processing");
reporter.success("Operation completed successfully");
reporter.error("Example error message");
}
#[test]
fn test_progress_styles() {
let _test_home = TestHomeGuard::new();
{
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Bytes).with_total(1_000_000);
progress.start(config);
progress.update(500_000, None);
progress.complete(None);
}
{
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Count).with_total(100);
progress.start(config);
progress.update(50, None);
progress.complete(None);
}
}
#[test]
fn test_progress_with_message_updates() {
let _test_home = TestHomeGuard::new();
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Count);
progress.start(config);
let messages = vec![
"Initializing...",
"Connecting to server...",
"Downloading metadata...",
"Processing data...",
"Finalizing...",
];
for msg in messages {
progress.set_message(msg.to_string());
thread::sleep(Duration::from_millis(10));
}
progress.complete(None);
}
#[test]
fn test_progress_indicator_memory_usage() {
let _test_home = TestHomeGuard::new();
for _ in 0..100 {
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Count).with_total(10);
progress.start(config);
progress.update(5, None);
progress.complete(None);
}
}
#[test]
fn test_progress_indicator_performance() {
let _test_home = TestHomeGuard::new();
let mut progress = ProgressFactory::create(true);
let config = ProgressConfig::new(ProgressStyle::Count).with_total(1_000_000);
let start = Instant::now();
progress.start(config);
for i in 0..1_000_000 {
if i % 10_000 == 0 {
progress.update(i, None);
}
}
progress.complete(None);
let elapsed = start.elapsed();
assert!(
elapsed < Duration::from_secs(1),
"Progress indicator overhead too high: {elapsed:?}"
);
}
#[test]
fn test_progress_with_long_operations() {
let _test_home = TestHomeGuard::new();
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Bytes).with_total(1_000_000_000);
progress.start(config);
let chunk_size = 100_000_000; for i in 0..10 {
progress.update(i * chunk_size, None);
thread::sleep(Duration::from_millis(5));
}
progress.complete(None);
}
#[test]
fn test_progress_indicator_state_transitions() {
let _test_home = TestHomeGuard::new();
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Count).with_total(100);
progress.start(config.clone());
progress.update(25, None);
progress.update(50, None);
progress.update(75, None);
progress.complete(None);
progress.start(config);
progress.update(100, None);
progress.complete(None);
}
#[test]
fn test_progress_indicator_zero_total() {
let _test_home = TestHomeGuard::new();
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Count).with_total(0);
progress.start(config);
progress.update(0, None);
progress.complete(None);
}
#[test]
fn test_progress_indicator_overflow_protection() {
let _test_home = TestHomeGuard::new();
let mut progress = ProgressFactory::create(false);
let config = ProgressConfig::new(ProgressStyle::Count).with_total(100);
progress.start(config);
progress.update(150, None);
progress.update(200, None);
progress.complete(None);
}
#[test]
#[serial]
fn test_metadata_fetch_progress_messages() {
use kopi::cache::fetch_and_cache_metadata_with_progress;
use kopi::config::new_kopi_config;
let test_home = TestHomeGuard::new();
let test_home = test_home.setup_kopi_structure();
unsafe {
std::env::set_var("KOPI_HOME", test_home.kopi_home().to_str().unwrap());
}
let config = new_kopi_config().unwrap();
let mut capture = TestProgressCapture::new();
let mut current_step = 0;
let _ = fetch_and_cache_metadata_with_progress(&config, &mut capture, &mut current_step);
let messages = capture.get_messages();
if !messages.is_empty() {
assert!(capture.message_count() > 0, "Should have progress messages");
let has_relevant_message = messages.iter().any(|m| {
m.message.contains("metadata")
|| m.message.contains("Fetch")
|| m.message.contains("source")
|| m.message.contains("cache")
|| m.message.contains("Processing")
});
assert!(
has_relevant_message,
"Should have relevant progress messages"
);
}
}
#[test]
fn test_metadata_progress_step_counting() {
let mut capture = TestProgressCapture::new();
capture.with_total(10);
capture.set_position(1);
capture.set_message("Initializing metadata provider".to_string());
for i in 2..=6 {
capture.set_position(i);
capture.set_message(format!("Fetching from source {}", i - 1));
}
capture.set_position(7);
capture.set_message("Processing metadata".to_string());
capture.set_position(8);
capture.set_message("Grouping distributions".to_string());
capture.set_position(9);
capture.set_message("Saving to cache".to_string());
capture.set_position(10);
capture.finish_with_message("Cache refresh complete");
assert_eq!(capture.get_position(), 10);
assert_eq!(capture.get_total(), Some(10));
assert_eq!(capture.message_count(), 10);
assert!(capture.contains_message("Fetching from source"));
assert!(capture.contains_message("Cache refresh complete"));
}
#[test]
fn test_metadata_progress_error_handling() {
let mut capture = TestProgressCapture::new();
capture.set_message("Starting metadata fetch".to_string());
capture.set_message("Connecting to API".to_string());
capture.error("Failed to connect to metadata source".to_string());
assert!(capture.contains_message("[ERROR]"));
assert!(capture.contains_message("Failed to connect"));
}
#[test]
fn test_silent_progress_with_metadata() {
use kopi::cache::fetch_and_cache_metadata_with_progress;
use kopi::config::new_kopi_config;
let test_home = TestHomeGuard::new();
let test_home = test_home.setup_kopi_structure();
unsafe {
std::env::set_var("KOPI_HOME", test_home.kopi_home().to_str().unwrap());
}
let config = new_kopi_config().unwrap();
let mut progress = SilentProgress;
let mut current_step = 0;
let _ = fetch_and_cache_metadata_with_progress(&config, &mut progress, &mut current_step);
}
#[test]
fn test_distribution_fetch_progress() {
let mut capture = TestProgressCapture::new();
capture.set_message("Fetching distribution: temurin".to_string());
capture.set_message("Querying API for temurin packages".to_string());
capture.set_message("Processing 25 packages".to_string());
capture.set_message("Updating cache with temurin data".to_string());
capture.finish_with_message("Distribution fetch complete");
assert_eq!(capture.message_count(), 5);
assert!(capture.contains_message("temurin"));
assert!(capture.contains_message("25 packages"));
}