use super::*;
use crate::gpu::GpuType;
#[test]
fn test_menu_config_default() {
let config = MenuConfig::new();
assert!(config.animation_speed() > 0.0);
assert!(config.max_visible_items() >= 5);
assert!(!config.has_user_overrides());
}
#[test]
fn default_config_dir_follows_host_convention() {
let dir = ConfigPersistence::new().config_dir().to_path_buf();
assert_eq!(dir.file_name().and_then(|n| n.to_str()), Some("rust-widgets"));
let Some(base) = dirs::config_dir() else {
return; };
assert_eq!(dir.parent(), Some(base.as_path()));
}
#[test]
fn hardware_detection_delegates_to_platform_backend() {
let backend_on_battery = crate::platform::platform_facts().is_on_battery();
let config = MenuConfig::new();
assert_eq!(
config.hardware_caps().on_battery,
backend_on_battery,
"on_battery must come from Platform::is_on_battery",
);
let reported = crate::platform::platform_facts().total_memory_mb();
assert_eq!(
config.hardware_caps().system_ram_mb,
reported,
"system_ram_mb must be the backend's own answer, `None` included",
);
}
#[test]
fn test_user_overrides() {
let mut config = MenuConfig::new();
config.set_animations_enabled(false);
config.set_transparency_enabled(true);
config.set_animation_speed(1.5);
assert!(config.has_user_overrides());
assert!(!config.animations_enabled());
assert!(config.transparency_enabled());
assert_eq!(config.animation_speed(), 1.5);
assert_eq!(config.user_overrides().animations, Some(false));
assert_eq!(config.user_overrides().transparency, Some(true));
}
#[test]
fn test_reset_to_defaults() {
let mut config = MenuConfig::new();
let original_animations = config.animations_enabled();
config.set_animations_enabled(!original_animations);
assert!(config.has_user_overrides());
config.reset_to_defaults();
assert!(!config.has_user_overrides());
assert_eq!(config.animations_enabled(), original_animations);
}
#[test]
fn test_animation_speed_clamping() {
let mut config = MenuConfig::new();
config.set_animation_speed(0.05);
assert_eq!(config.animation_speed(), 0.1);
config.set_animation_speed(5.0);
assert_eq!(config.animation_speed(), 3.0);
config.set_animation_speed(1.5);
assert_eq!(config.animation_speed(), 1.5);
}
#[test]
fn test_max_visible_items_minimum() {
let mut config = MenuConfig::new();
config.set_max_visible_items(2);
assert_eq!(config.max_visible_items(), 5);
config.set_max_visible_items(30);
assert_eq!(config.max_visible_items(), 30);
}
#[test]
fn test_settings_summary() {
let config = MenuConfig::new();
let summary = config.settings_summary();
assert!(summary.contains("Menu Settings:"));
assert!(summary.contains("Animations:"));
assert!(summary.contains("Transparency:"));
assert!(summary.contains("Performance Level:"));
}
#[test]
fn test_config_manager() {
let mut manager = MenuConfigManager::new();
assert!(manager.auto_adjust());
manager.set_auto_adjust(false);
assert!(!manager.auto_adjust());
let _ = manager.config().animations_enabled();
}
#[test]
fn test_config_persistence_roundtrip() {
let temp_dir = tempfile::TempDir::new().expect("temp dir must be created");
let persistence = ConfigPersistence::with_dir(temp_dir.path().to_path_buf());
let mut config = MenuConfig::new();
config.set_animations_enabled(false);
config.set_transparency_enabled(true);
config.set_animation_speed(1.5);
persistence.save(&config).unwrap();
assert!(persistence.exists());
let overrides = persistence.load().unwrap();
assert_eq!(overrides.animations, Some(false));
assert_eq!(overrides.transparency, Some(true));
assert_eq!(overrides.animation_speed, Some(1.5));
}
#[test]
fn test_config_dialog() {
let config = MenuConfig::new();
let mut dialog = MenuConfigDialog::new(config);
let initial_animations = dialog.config().animations_enabled();
dialog.toggle_animations();
assert_eq!(dialog.config().animations_enabled(), !initial_animations);
let initial_speed = dialog.config().animation_speed();
dialog.increase_animation_speed();
assert!(dialog.config().animation_speed() > initial_speed);
dialog.decrease_animation_speed();
assert!(dialog.config().animation_speed() <= initial_speed + 0.1);
let initial_max = dialog.config().max_visible_items();
dialog.increase_max_items();
assert_eq!(dialog.config().max_visible_items(), initial_max + 5);
dialog.decrease_max_items();
assert_eq!(dialog.config().max_visible_items(), initial_max);
dialog.reset_to_defaults();
assert!(!dialog.has_overrides());
let summary = dialog.settings_summary();
assert!(summary.contains("Menu Settings:"));
assert!(summary.contains("Performance Level:"));
let gpu_desc = dialog.gpu_description();
assert!(!gpu_desc.is_empty());
}
#[test]
fn test_performance_level_enum() {
assert_ne!(PerformanceLevel::Low, PerformanceLevel::Medium);
assert_ne!(PerformanceLevel::Medium, PerformanceLevel::High);
assert!(matches!(PerformanceLevel::default(), PerformanceLevel::Medium));
}
#[test]
fn test_hardware_capabilities() {
let caps = HardwareCapabilities {
gpu_type: GpuType::Discrete,
gpu_memory_mb: Some(4096),
gpu_memory_is_measured: true,
gpu_performance_score: 80,
system_ram_mb: Some(16384),
cpu_performance_score: 70,
on_battery: false,
performance_level: PerformanceLevel::High,
};
assert!(matches!(caps.gpu_type, GpuType::Discrete));
assert_eq!(caps.gpu_memory_mb, Some(4096));
assert!(caps.gpu_memory_is_measured);
assert!(!caps.on_battery);
assert!(matches!(caps.performance_level, PerformanceLevel::High));
}
#[test]
fn test_unmeasured_gpu_memory_is_not_reported_as_detected() {
let caps = HardwareCapabilities {
gpu_type: GpuType::Integrated,
gpu_memory_mb: None,
gpu_memory_is_measured: false,
gpu_performance_score: 40,
system_ram_mb: Some(8192),
cpu_performance_score: 50,
on_battery: false,
performance_level: PerformanceLevel::Medium,
};
assert_eq!(caps.gpu_memory_mb, None);
assert!(!caps.gpu_memory_is_measured);
assert_eq!(MenuConfig::assumed_gpu_memory(&GpuType::Integrated), 512);
assert_eq!(MenuConfig::assumed_gpu_memory(&GpuType::Discrete), 4096);
assert_eq!(MenuConfig::assumed_gpu_memory(&GpuType::Cpu), 0);
}
#[test]
fn test_gpu_memory_override_is_used_when_present_and_absent_means_unknown() {
let previous = std::env::var("RUST_WIDGETS_GPU_MEMORY_MB").ok();
std::env::remove_var("RUST_WIDGETS_GPU_MEMORY_MB");
assert_eq!(MenuConfig::detect_gpu_memory_for_test(), None);
std::env::set_var("RUST_WIDGETS_GPU_MEMORY_MB", "8192");
assert_eq!(MenuConfig::detect_gpu_memory_for_test(), Some(8192));
std::env::set_var("RUST_WIDGETS_GPU_MEMORY_MB", "0");
assert_eq!(MenuConfig::detect_gpu_memory_for_test(), None);
std::env::set_var("RUST_WIDGETS_GPU_MEMORY_MB", "not-a-number");
assert_eq!(MenuConfig::detect_gpu_memory_for_test(), None);
match previous {
Some(value) => std::env::set_var("RUST_WIDGETS_GPU_MEMORY_MB", value),
None => std::env::remove_var("RUST_WIDGETS_GPU_MEMORY_MB"),
}
}
#[test]
fn persistence_round_trips_every_user_override() {
let temp_dir = tempfile::TempDir::new().expect("temp dir must be created");
let persistence = ConfigPersistence::with_dir(temp_dir.path().to_path_buf());
let mut config = MenuConfig::new();
config.set_animation_speed(1.25);
config.set_max_visible_items(12);
config.set_animations_enabled(false);
config.set_hardware_acceleration(true);
persistence.save(&config).expect("save must reach the temp dir");
let loaded = persistence.load().expect("load must read back what save wrote");
assert_eq!(loaded.animation_speed, Some(1.25), "animation_speed must round-trip");
assert_eq!(loaded.max_visible_items, Some(12), "max_visible_items must round-trip");
assert_eq!(loaded.animations, Some(false), "animations must round-trip");
assert_eq!(loaded.hardware_acceleration, Some(true), "hardware_acceleration must round-trip");
persistence.clear().expect("clear must succeed on a saved config");
assert!(!persistence.exists(), "clear must remove the config file");
}
#[test]
fn animation_speed_rejects_nan_and_saturates_non_finite() {
let mut config = MenuConfig::new();
config.set_animation_speed(1.5);
assert_eq!(config.animation_speed(), 1.5);
config.set_animation_speed(f32::NAN);
assert_eq!(config.animation_speed(), 1.5);
assert_eq!(config.user_overrides().animation_speed, Some(1.5));
config.set_animation_speed(f32::INFINITY);
assert_eq!(config.animation_speed(), 3.0);
config.set_animation_speed(f32::NEG_INFINITY);
assert_eq!(config.animation_speed(), 0.1);
config.set_animation_speed(99.0);
assert_eq!(config.animation_speed(), 3.0);
}
#[test]
fn load_reports_known_field_parse_errors() {
let temp_dir = tempfile::TempDir::new().expect("temp dir must be created");
let persistence = ConfigPersistence::with_dir(temp_dir.path().to_path_buf());
std::fs::write(
temp_dir.path().join("menu_config.json"),
"animations_enabled=not-a-bool\nunknown_key=whatever\n",
)
.unwrap();
match persistence.load() {
Err(ConfigLoadError::Parse { line, field, value }) => {
assert_eq!(line, 1, "the error must name the line");
assert_eq!(field, "animations_enabled", "the error must name the field");
assert_eq!(value, "not-a-bool", "the error must name the value");
}
other => panic!("expected a known-field parse error, got {other:?}"),
}
}
#[test]
fn rejected_load_keeps_previous_config() {
let temp_dir = tempfile::TempDir::new().expect("temp dir must be created");
let persistence = ConfigPersistence::with_dir(temp_dir.path().to_path_buf());
let mut config = MenuConfig::new();
config.set_animation_speed(2.0);
let mut dialog = MenuConfigDialog::with_persistence(config, persistence);
std::fs::write(temp_dir.path().join("menu_config.json"), "max_visible_items=bad\n").unwrap();
assert!(dialog.load().is_err(), "a known-field parse error must reject the load");
assert_eq!(
dialog.config().animation_speed(),
2.0,
"previous config must survive a rejected load"
);
}
#[test]
fn reload_restores_baseline_for_deleted_fields() {
let temp_dir = tempfile::TempDir::new().expect("temp dir must be created");
let persistence = ConfigPersistence::with_dir(temp_dir.path().to_path_buf());
let mut config = MenuConfig::new();
let baseline_animations = config.animations_enabled();
config.set_animations_enabled(!baseline_animations);
std::fs::write(temp_dir.path().join("menu_config.json"), "transparency_enabled=true\n")
.unwrap();
let mut dialog = MenuConfigDialog::with_persistence(config, persistence);
dialog.load().expect("load must succeed");
assert_eq!(dialog.config().animations_enabled(), baseline_animations);
assert_eq!(dialog.config().user_overrides().animations, None);
assert!(dialog.config().transparency_enabled());
assert_eq!(dialog.config().user_overrides().transparency, Some(true));
}
#[test]
fn load_missing_file_restores_baseline_and_clears_overrides() {
let temp_dir = tempfile::TempDir::new().expect("temp dir must be created");
let persistence = ConfigPersistence::with_dir(temp_dir.path().to_path_buf());
let mut config = MenuConfig::new();
let baseline_animations = config.animations_enabled();
config.set_animations_enabled(!baseline_animations);
assert!(config.has_user_overrides());
let mut dialog = MenuConfigDialog::with_persistence(config, persistence);
dialog.load().expect("load of a missing file yields defaults");
assert!(!dialog.has_overrides());
assert_eq!(dialog.config().animations_enabled(), baseline_animations);
}
#[test]
fn increase_max_items_saturates_at_maximum() {
let mut config = MenuConfig::new();
config.set_max_visible_items(u32::MAX);
assert_eq!(config.max_visible_items(), u32::MAX);
let mut dialog = MenuConfigDialog::new(config);
dialog.increase_max_items();
assert_eq!(dialog.config().max_visible_items(), u32::MAX);
}