additional test coverage for missing lines #11

Merged
JMR-dev merged 1 commits from fix-improve-test-coverage into main 2025-10-07 22:34:29 +00:00
4 changed files with 3225 additions and 0 deletions
+2611
View File
File diff suppressed because it is too large Load Diff
+161
View File
@@ -0,0 +1,161 @@
/// Additional tests for config module to improve coverage
use redshift_rebooted::config::{Config, LocationSource};
use redshift_rebooted::types::Location;
#[test]
fn test_config_path_creation() {
let result = Config::config_path();
assert!(result.is_ok(), "Should be able to determine config path");
let path = result.unwrap();
assert!(path.to_string_lossy().contains("redshift"), "Path should contain 'redshift'");
assert!(path.to_string_lossy().ends_with("config.toml"), "Path should end with 'config.toml'");
}
#[test]
fn test_config_load_returns_default_on_missing_file() {
// Test the default config behavior
let config = Config::default();
assert!(config.location.is_none(), "Default config should have no location");
assert!(config.last_geoclue_check.is_none(), "Default config should have no last check");
}
#[test]
fn test_config_save_and_load() {
use std::time::{SystemTime, UNIX_EPOCH};
// Create a config with data
let mut config = Config::default();
config.set_location(
Location { lat: 48.8566, lon: 2.3522 },
LocationSource::Manual,
Some("Paris".to_string())
);
config.update_geoclue_check();
// Serialize to TOML string
let toml_str = toml::to_string(&config).expect("Should serialize to TOML");
// Deserialize back
let loaded: Config = toml::from_str(&toml_str).expect("Should deserialize from TOML");
// Verify data was preserved
assert!(loaded.location.is_some(), "Loaded config should have location");
let location = loaded.get_location().unwrap();
assert_eq!(location.lat, 48.8566, "Latitude should match");
assert_eq!(location.lon, 2.3522, "Longitude should match");
assert!(loaded.last_geoclue_check.is_some(), "Loaded config should have geoclue check timestamp");
if let Some(ref saved_loc) = loaded.location {
assert_eq!(saved_loc.source, LocationSource::Manual, "Source should be Manual");
assert_eq!(saved_loc.city_name, Some("Paris".to_string()), "City name should be preserved");
}
}
#[test]
fn test_config_path_has_parent_directory() {
// Verify config path has a parent directory
let config_path = Config::config_path();
assert!(config_path.is_ok(), "Should be able to get config path");
let path = config_path.unwrap();
assert!(path.parent().is_some(), "Config path should have a parent directory");
}
#[test]
fn test_config_update_geoclue_check() {
let mut config = Config::default();
// Initially should be None
assert!(config.last_geoclue_check.is_none());
// Update the check
config.update_geoclue_check();
// Should now have a timestamp
assert!(config.last_geoclue_check.is_some());
let timestamp = config.last_geoclue_check.unwrap();
// Timestamp should be recent (within last minute)
use std::time::{SystemTime, UNIX_EPOCH};
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
assert!(timestamp <= now, "Timestamp should not be in the future");
assert!(now - timestamp < 60, "Timestamp should be recent");
}
#[test]
fn test_config_parse_invalid_toml() {
// Test that invalid TOML fails to parse
let invalid_toml = "this is not valid toml [[[";
let result: Result<Config, _> = toml::from_str(invalid_toml);
assert!(result.is_err(), "Parsing invalid TOML should fail");
}
#[test]
fn test_location_source_serialization() {
use serde_json;
// Test Manual source
let manual_json = serde_json::to_string(&LocationSource::Manual).unwrap();
assert_eq!(manual_json, r#""manual""#);
// Test Interactive source
let interactive_json = serde_json::to_string(&LocationSource::Interactive).unwrap();
assert_eq!(interactive_json, r#""interactive""#);
// Test GeoClue2 source
let geoclue_json = serde_json::to_string(&LocationSource::GeoClue2).unwrap();
assert_eq!(geoclue_json, r#""geoclue2""#);
}
#[test]
fn test_location_source_deserialization() {
use serde_json;
let manual: LocationSource = serde_json::from_str(r#""manual""#).unwrap();
assert_eq!(manual, LocationSource::Manual);
let interactive: LocationSource = serde_json::from_str(r#""interactive""#).unwrap();
assert_eq!(interactive, LocationSource::Interactive);
let geoclue: LocationSource = serde_json::from_str(r#""geoclue2""#).unwrap();
assert_eq!(geoclue, LocationSource::GeoClue2);
}
#[test]
fn test_saved_location_with_all_fields() {
let mut config = Config::default();
config.set_location(
Location { lat: 35.6762, lon: 139.6503 },
LocationSource::GeoClue2,
Some("Tokyo".to_string())
);
let saved_loc = config.location.as_ref().unwrap();
assert_eq!(saved_loc.lat, 35.6762);
assert_eq!(saved_loc.lon, 139.6503);
assert_eq!(saved_loc.source, LocationSource::GeoClue2);
assert_eq!(saved_loc.city_name, Some("Tokyo".to_string()));
}
#[test]
fn test_saved_location_without_city_name() {
let mut config = Config::default();
config.set_location(
Location { lat: -33.8688, lon: 151.2093 },
LocationSource::Interactive,
None
);
let saved_loc = config.location.as_ref().unwrap();
assert_eq!(saved_loc.lat, -33.8688);
assert_eq!(saved_loc.lon, 151.2093);
assert_eq!(saved_loc.source, LocationSource::Interactive);
assert!(saved_loc.city_name.is_none());
}
+198
View File
@@ -0,0 +1,198 @@
/// Additional tests for gamma module to improve coverage
use redshift_rebooted::gamma::{DummyGammaMethod, GammaMethod};
use redshift_rebooted::types::ColorSetting;
#[test]
fn test_dummy_gamma_method_name() {
let method = DummyGammaMethod::new();
assert_eq!(method.name(), "dummy");
}
#[test]
fn test_dummy_gamma_method_print_help() {
let method = DummyGammaMethod::new();
// Should not panic
method.print_help();
}
#[test]
fn test_dummy_gamma_method_with_preserve_flag() {
let mut method = DummyGammaMethod::new();
method.init().expect("Init should succeed");
method.start().expect("Start should succeed");
let setting = ColorSetting {
temperature: 3500,
brightness: 1.0,
gamma: [1.0, 1.0, 1.0],
};
// Test with preserve = true
let result = method.set_temperature(&setting, true);
assert!(result.is_ok(), "set_temperature with preserve=true should succeed");
// Test with preserve = false
let result = method.set_temperature(&setting, false);
assert!(result.is_ok(), "set_temperature with preserve=false should succeed");
}
#[test]
fn test_dummy_gamma_method_extreme_temperatures() {
let mut method = DummyGammaMethod::new();
method.init().expect("Init should succeed");
method.start().expect("Start should succeed");
// Very cool temperature
let cool_setting = ColorSetting {
temperature: 1000,
brightness: 1.0,
gamma: [1.0, 1.0, 1.0],
};
let result = method.set_temperature(&cool_setting, false);
assert!(result.is_ok(), "Very cool temperature should succeed");
// Very warm temperature
let warm_setting = ColorSetting {
temperature: 25000,
brightness: 1.0,
gamma: [1.0, 1.0, 1.0],
};
let result = method.set_temperature(&warm_setting, false);
assert!(result.is_ok(), "Very warm temperature should succeed");
}
#[test]
fn test_dummy_gamma_method_various_brightness() {
let mut method = DummyGammaMethod::new();
method.init().expect("Init should succeed");
method.start().expect("Start should succeed");
let brightnesses = [0.1, 0.5, 0.8, 1.0];
for brightness in brightnesses {
let setting = ColorSetting {
temperature: 6500,
brightness,
gamma: [1.0, 1.0, 1.0],
};
let result = method.set_temperature(&setting, false);
assert!(result.is_ok(), "Brightness {} should succeed", brightness);
}
}
#[test]
fn test_dummy_gamma_method_various_gamma_values() {
let mut method = DummyGammaMethod::new();
method.init().expect("Init should succeed");
method.start().expect("Start should succeed");
let gamma_values = [
[0.5, 0.5, 0.5],
[1.0, 1.0, 1.0],
[1.5, 1.5, 1.5],
[2.0, 2.0, 2.0],
[1.0, 1.2, 0.8], // Asymmetric gamma
];
for gamma in gamma_values {
let setting = ColorSetting {
temperature: 6500,
brightness: 1.0,
gamma,
};
let result = method.set_temperature(&setting, false);
assert!(result.is_ok(), "Gamma {:?} should succeed", gamma);
}
}
#[test]
fn test_dummy_gamma_method_multiple_restore_calls() {
let mut method = DummyGammaMethod::new();
method.init().expect("Init should succeed");
method.start().expect("Start should succeed");
// Multiple restore calls should not panic
method.restore();
method.restore();
method.restore();
}
#[test]
fn test_dummy_gamma_method_restore_without_start() {
let mut method = DummyGammaMethod::new();
// Restore without start should not panic
method.restore();
}
#[test]
fn test_dummy_gamma_method_as_trait_object() {
let mut method: Box<dyn GammaMethod> = Box::new(DummyGammaMethod::new());
assert!(method.init().is_ok());
assert!(method.start().is_ok());
assert_eq!(method.name(), "dummy");
let setting = ColorSetting::default();
assert!(method.set_temperature(&setting, false).is_ok());
method.restore();
method.print_help();
}
#[test]
fn test_dummy_gamma_method_sequence_of_different_settings() {
let mut method = DummyGammaMethod::new();
method.init().expect("Init should succeed");
method.start().expect("Start should succeed");
// Sequence of different settings simulating a day cycle
let settings = [
ColorSetting { temperature: 6500, brightness: 0.5, gamma: [1.0, 1.0, 1.0] },
ColorSetting { temperature: 5000, brightness: 0.7, gamma: [1.0, 1.0, 1.0] },
ColorSetting { temperature: 4000, brightness: 0.9, gamma: [1.0, 1.0, 1.0] },
ColorSetting { temperature: 3500, brightness: 1.0, gamma: [1.0, 1.0, 1.0] },
ColorSetting { temperature: 4000, brightness: 0.9, gamma: [1.0, 1.0, 1.0] },
ColorSetting { temperature: 5000, brightness: 0.7, gamma: [1.0, 1.0, 1.0] },
ColorSetting { temperature: 6500, brightness: 0.5, gamma: [1.0, 1.0, 1.0] },
];
for setting in &settings {
let result = method.set_temperature(setting, false);
assert!(result.is_ok(), "Setting temperature to {} should succeed", setting.temperature);
}
}
#[test]
fn test_dummy_gamma_method_init_multiple_times() {
let mut method = DummyGammaMethod::new();
// Init multiple times should succeed
assert!(method.init().is_ok());
assert!(method.init().is_ok());
assert!(method.init().is_ok());
}
#[test]
fn test_dummy_gamma_method_start_multiple_times() {
let mut method = DummyGammaMethod::new();
method.init().expect("Init should succeed");
// Start multiple times should succeed
assert!(method.start().is_ok());
assert!(method.start().is_ok());
}
#[test]
fn test_color_setting_default_for_gamma_method() {
let mut method = DummyGammaMethod::new();
method.init().expect("Init should succeed");
method.start().expect("Start should succeed");
// Test with default ColorSetting
let setting = ColorSetting::default();
let result = method.set_temperature(&setting, false);
assert!(result.is_ok(), "Default ColorSetting should work");
}
+255
View File
@@ -0,0 +1,255 @@
/// Additional tests for solar module to improve coverage
// Import private functions for testing by using path
// Since these are in the main module, we need to test them indirectly
#[test]
fn test_solar_constants_are_defined() {
// Test that the solar constants are accessible and correct
use redshift_rebooted::solar::{
SOLAR_ATM_REFRAC, SOLAR_ASTRO_TWILIGHT_ELEV, SOLAR_CIVIL_TWILIGHT_ELEV,
SOLAR_DAYTIME_ELEV, SOLAR_NAUT_TWILIGHT_ELEV,
};
assert_eq!(SOLAR_ATM_REFRAC, 0.833);
assert_eq!(SOLAR_ASTRO_TWILIGHT_ELEV, -18.0);
assert_eq!(SOLAR_NAUT_TWILIGHT_ELEV, -12.0);
assert_eq!(SOLAR_CIVIL_TWILIGHT_ELEV, -6.0);
assert_eq!(SOLAR_DAYTIME_ELEV, 0.0 - SOLAR_ATM_REFRAC);
}
#[test]
fn test_solar_elevation_is_computed() {
// Test that solar elevation can be computed without errors
// Use equator at noon UTC for simplicity
// March 20, 2024, ~12:00 UTC (approximate equinox)
let date = 1710936000.0; // Unix timestamp
let lat = 0.0; // Equator
let lon = 0.0; // Prime meridian
let elevation = redshift_rebooted::solar::solar_elevation(date, lat, lon);
// Elevation should be in valid range
assert!(elevation >= -90.0 && elevation <= 90.0, "Elevation should be in valid range");
}
#[test]
fn test_solar_elevation_at_midnight() {
// Test solar elevation at midnight (lowest point)
// Should be negative (sun below horizon)
// March 20, 2024, ~00:00 UTC
let date = 1710892800.0; // Unix timestamp
let lat = 40.7;
let lon = -74.0;
let elevation = redshift_rebooted::solar::solar_elevation(date, lat, lon);
// At midnight, sun should be below horizon
assert!(elevation < 0.0, "Solar elevation at midnight should be negative");
}
#[test]
fn test_solar_table_fill_returns_all_times() {
// Test that solar_table_fill returns a complete table
use redshift_rebooted::solar::solar_table_fill;
let date = 1710936000.0; // March 20, 2024, ~12:00 UTC
let lat = 40.7;
let lon = -74.0;
let table = solar_table_fill(date, lat, lon);
// Table should have 10 entries
assert_eq!(table.len(), 10);
// Noon and midnight should always be valid
assert!(!table[0].is_nan(), "Noon should be valid"); // Noon is index 0
assert!(!table[1].is_nan(), "Midnight should be valid"); // Midnight is index 1
}
#[test]
fn test_solar_table_fill_has_valid_noon() {
// Test that solar_table_fill returns a valid noon timestamp
use redshift_rebooted::solar::solar_table_fill;
let date = 1710936000.0; // March 20, 2024
let lat = 40.7;
let lon = -74.0;
let table = solar_table_fill(date, lat, lon);
// Noon should always be valid
let noon = table[0];
assert!(!noon.is_nan(), "Noon should be valid");
assert!(noon > 0.0, "Noon timestamp should be positive");
}
#[test]
fn test_solar_table_fill_polar_latitudes() {
// Test solar_table_fill at polar latitudes where some events may not occur
use redshift_rebooted::solar::solar_table_fill;
// North pole in summer - midnight sun
let date = 1718985600.0; // June 21, 2024 (summer solstice)
let lat = 85.0; // Near north pole
let lon = 0.0;
let table = solar_table_fill(date, lat, lon);
// Some twilight events may be NaN (sun never goes below certain elevations)
// Just verify the function completes without panic
assert_eq!(table.len(), 10);
}
#[test]
fn test_solar_table_fill_equator() {
// Test solar_table_fill at the equator
use redshift_rebooted::solar::solar_table_fill;
let date = 1710936000.0; // March 20, 2024 (equinox)
let lat = 0.0; // Equator
let lon = 0.0;
let table = solar_table_fill(date, lat, lon);
// All events should be valid at equator on equinox
for (i, &time) in table.iter().enumerate() {
assert!(!time.is_nan(), "Event {} should be valid at equator", i);
}
}
#[test]
fn test_solar_elevation_changes_over_time() {
// Test that solar elevation changes over a 24-hour period
use redshift_rebooted::solar::solar_elevation;
let lat = 0.0; // Equator for simpler calculations
let lon = 0.0;
// Sample at different times
let time1 = 1710892800.0;
let time2 = 1710935400.0; // 12 hours later
let el1 = solar_elevation(time1, lat, lon);
let el2 = solar_elevation(time2, lat, lon);
// Elevations should be different
assert_ne!(el1, el2, "Solar elevation should change over time");
assert!(el1 >= -90.0 && el1 <= 90.0, "Elevation 1 should be in valid range");
assert!(el2 >= -90.0 && el2 <= 90.0, "Elevation 2 should be in valid range");
}
#[test]
fn test_solar_elevation_different_times_differ() {
// Test that solar elevation differs at different times
use redshift_rebooted::solar::solar_elevation;
let lat = 40.7;
let lon = -74.0;
// Different times of day
let time1 = 1710936000.0;
let time2 = 1710946800.0; // Several hours later
let time3 = 1710957600.0; // Several more hours later
let el1 = solar_elevation(time1, lat, lon);
let el2 = solar_elevation(time2, lat, lon);
let el3 = solar_elevation(time3, lat, lon);
// At least some values should differ
assert!(el1 != el2 || el2 != el3, "Solar elevations should vary over time");
}
#[test]
fn test_solar_elevation_southern_hemisphere() {
// Test solar elevation in southern hemisphere
use redshift_rebooted::solar::solar_elevation;
// Sydney, Australia (-33.9° S, 151.2° E)
let date = 1710936000.0;
let lat = -33.9;
let lon = 151.2;
let elevation = solar_elevation(date, lat, lon);
// Should get a valid elevation
assert!(elevation > -90.0 && elevation < 90.0, "Elevation should be in valid range");
}
#[test]
fn test_solar_elevation_negative_longitude() {
// Test with negative longitude (western hemisphere)
use redshift_rebooted::solar::solar_elevation;
let date = 1710936000.0;
let lat = 51.5; // London
let lon = -0.1; // Slightly west of prime meridian
let elevation = solar_elevation(date, lat, lon);
// Should get a valid elevation
assert!(elevation > -90.0 && elevation < 90.0, "Elevation should be in valid range");
}
#[test]
fn test_solar_table_fill_midnight_after_noon() {
// Verify that midnight timestamp is 12 hours after noon
use redshift_rebooted::solar::solar_table_fill;
let date = 1710936000.0;
let lat = 40.7;
let lon = -74.0;
let table = solar_table_fill(date, lat, lon);
let noon = table[0];
let midnight = table[1];
// Midnight should be approximately 12 hours (43200 seconds) after noon
let diff = midnight - noon;
assert!((diff - 43200.0).abs() < 60.0, "Midnight should be ~12 hours after noon");
}
#[test]
fn test_solar_elevation_extreme_latitudes() {
// Test solar elevation calculations at extreme latitudes
use redshift_rebooted::solar::solar_elevation;
let date = 1710936000.0;
// Near north pole
let el_north = solar_elevation(date, 89.0, 0.0);
assert!(el_north > -90.0 && el_north < 90.0, "North pole elevation should be valid");
// Near south pole
let el_south = solar_elevation(date, -89.0, 0.0);
assert!(el_south > -90.0 && el_south < 90.0, "South pole elevation should be valid");
}
#[test]
fn test_solar_elevation_full_day_cycle() {
// Test that solar elevation follows expected pattern over 24 hours
use redshift_rebooted::solar::solar_elevation;
let lat = 40.7;
let lon = -74.0;
let start_date = 1710892800.0; // Midnight
let mut max_elevation: f64 = -90.0;
let mut min_elevation: f64 = 90.0;
// Sample every 2 hours for 24 hours
for hour in 0..12 {
let date = start_date + (hour as f64 * 7200.0);
let el = solar_elevation(date, lat, lon);
max_elevation = max_elevation.max(el);
min_elevation = min_elevation.min(el);
}
// Max should be positive (daytime), min should be negative (nighttime)
assert!(max_elevation > 0.0, "Max elevation during day should be positive");
assert!(min_elevation < 0.0, "Min elevation during night should be negative");
}