use metfor::{Celsius, HectoPascal, Knots, Meters, WindSpdDir};
use optional::Optioned;
use sounding_base::{Sounding, StationInfo};
use sounding_validate::{validate, ValidationError};
#[test]
fn test_validate() {
let snd = create_valid_test_sounding();
let result = validate(&snd);
if result.is_err() {
println!("{:?}", result);
}
assert!(result.is_ok());
}
fn create_valid_test_sounding() -> Sounding {
Sounding::new()
.with_station_info(StationInfo::new_with_values(
1,
(45.0, -115.0),
Meters(1023.0),
))
.with_valid_time(None)
.with_lead_time(0)
.with_pressure_profile(vec![
Optioned::from(HectoPascal(840.0)),
Optioned::from(HectoPascal(800.0)),
Optioned::from(HectoPascal(700.0)),
Optioned::from(HectoPascal(500.0)),
Optioned::from(HectoPascal(300.0)),
Optioned::from(HectoPascal(250.0)),
Optioned::from(HectoPascal(200.0)),
Optioned::from(HectoPascal(100.0)),
])
.with_temperature_profile(vec![
Optioned::from(Celsius(20.0)),
Optioned::from(Celsius(15.0)),
Optioned::from(Celsius(2.0)),
Optioned::from(Celsius(-10.0)),
Optioned::from(Celsius(-20.0)),
Optioned::from(Celsius(-30.0)),
Optioned::from(Celsius(-50.0)),
Optioned::from(Celsius(-45.0)),
])
.with_wet_bulb_profile(vec![
Optioned::from(Celsius(20.0)),
Optioned::from(Celsius(14.0)),
Optioned::from(Celsius(1.0)),
Optioned::from(Celsius(-11.0)),
Optioned::from(Celsius(-25.0)),
Optioned::from(Celsius(-39.0)),
Optioned::from(Celsius(-58.0)),
Optioned::from(Celsius(-60.0)),
])
.with_dew_point_profile(vec![
Optioned::from(Celsius(20.0)),
Optioned::from(Celsius(13.0)),
Optioned::from(Celsius(0.0)),
Optioned::from(Celsius(-12.0)),
Optioned::from(Celsius(-27.0)),
Optioned::from(Celsius(-45.0)),
Optioned::from(Celsius(-62.0)),
Optioned::from(Celsius(-80.0)),
])
.with_wind_profile(vec![
Optioned::from(WindSpdDir {
direction: 0.0,
speed: Knots(5.0),
}),
Optioned::from(WindSpdDir {
direction: 40.0,
speed: Knots(10.0),
}),
Optioned::from(WindSpdDir {
direction: 80.0,
speed: Knots(15.0),
}),
Optioned::from(WindSpdDir {
direction: 120.0,
speed: Knots(12.0),
}),
Optioned::from(WindSpdDir {
direction: 160.0,
speed: Knots(27.0),
}),
Optioned::from(WindSpdDir {
direction: 200.0,
speed: Knots(45.0),
}),
Optioned::from(WindSpdDir {
direction: 240.0,
speed: Knots(62.0),
}),
Optioned::from(WindSpdDir {
direction: 280.0,
speed: Knots(80.0),
}),
])
.with_height_profile(vec![
Optioned::from(Meters(1050.0)),
Optioned::from(Meters(2000.0)),
Optioned::from(Meters(3000.0)),
Optioned::from(Meters(4000.0)),
Optioned::from(Meters(5000.0)),
Optioned::from(Meters(6500.0)),
Optioned::from(Meters(7000.0)),
Optioned::from(Meters(8000.0)),
])
.with_cloud_fraction_profile(vec![
Optioned::from(100.0),
Optioned::from(85.0),
Optioned::from(70.0),
Optioned::from(50.0),
Optioned::from(30.0),
Optioned::from(25.0),
Optioned::from(20.0),
Optioned::from(10.0),
])
.with_mslp(HectoPascal(1014.0))
.with_station_pressure(HectoPascal(847.0))
.with_sfc_wind(WindSpdDir {
speed: Knots(0.0),
direction: 0.0,
})
}
#[test]
fn test_pressure_not_decreasing_with_height() {
let snd = create_invalid_test_sounding_pressure_not_decreasing_with_height();
let result = validate(&snd);
assert!(result.is_err());
assert!(result
.unwrap_err()
.into_inner()
.contains(&ValidationError::PressureNotDecreasingWithHeight));
}
fn create_invalid_test_sounding_pressure_not_decreasing_with_height() -> Sounding {
create_valid_test_sounding().with_station_pressure(HectoPascal(830.0))
}
#[test]
fn test_no_pressure_profile() {
let snd = create_invalid_test_sounding_no_pressure_profile();
let result = validate(&snd);
let err = result.unwrap_err();
println!("{}", err);
assert!(err
.into_inner()
.contains(&ValidationError::NoPressureProfile));
}
fn create_invalid_test_sounding_no_pressure_profile() -> Sounding {
create_valid_test_sounding().with_pressure_profile(vec![])
}
#[test]
fn test_no_invalid_vector_length() {
let snd = create_invalid_test_sounding_vector_legth();
let result = validate(&snd);
let err = result.unwrap_err();
println!("{}", err);
if let ValidationError::InvalidVectorLength(desc, actual_length, desired_length) =
err.into_inner()[0]
{
assert_eq!(desc, "Cloud fraction");
assert_eq!(actual_length, 7);
assert_eq!(desired_length, 9);
} else {
panic!("Error is of wrong type!");
}
}
fn create_invalid_test_sounding_vector_legth() -> Sounding {
let cloud_profile = vec![
Optioned::from(20.0),
Optioned::from(15.0),
Optioned::from(2.0),
Optioned::from(10.0),
Optioned::from(20.0),
Optioned::from(30.0),
];
create_valid_test_sounding().with_cloud_fraction_profile(cloud_profile)
}
#[test]
fn test_temperature_less_than_wetbulb() {
let snd = create_invalid_test_temperature_less_than_wetbulb();
let result = validate(&snd);
let err = result.unwrap_err();
println!("{}", err);
let errs = err.into_inner();
assert!(errs.len() == 3);
for err in errs {
if let ValidationError::TemperatureLessThanWetBulb(t, wb) = err {
assert!(t < wb);
} else {
panic!("Error is of wrong type!");
}
}
}
fn create_invalid_test_temperature_less_than_wetbulb() -> Sounding {
let t_profile = vec![
Optioned::from(Celsius(20.0)),
Optioned::from(Celsius(13.0)),
Optioned::from(Celsius(0.0)),
Optioned::from(Celsius(-12.0)),
Optioned::from(Celsius(-20.0)),
Optioned::from(Celsius(-30.0)),
Optioned::from(Celsius(-50.0)),
Optioned::from(Celsius(-45.0)),
];
create_valid_test_sounding().with_temperature_profile(t_profile)
}
#[test]
fn test_temperature_less_than_dewpoint() {
let snd = create_invalid_test_temperature_less_than_dewpoint();
let result = validate(&snd);
let err = result.unwrap_err();
println!("{}", err);
let errs = err.into_inner();
assert!(errs.len() == 6);
for err in errs {
if let ValidationError::TemperatureLessThanDewPoint(t, dp) = err {
assert!(t < dp);
} else if let ValidationError::TemperatureLessThanWetBulb(t, wb) = err {
assert!(t < wb);
} else {
panic!("Error is of wrong type!");
}
}
}
fn create_invalid_test_temperature_less_than_dewpoint() -> Sounding {
let t_profile = vec![
Optioned::from(Celsius(20.0)),
Optioned::from(Celsius(10.0)),
Optioned::from(Celsius(-1.0)),
Optioned::from(Celsius(-13.0)),
Optioned::from(Celsius(-20.0)),
Optioned::from(Celsius(-30.0)),
Optioned::from(Celsius(-50.0)),
Optioned::from(Celsius(-45.0)),
];
create_valid_test_sounding().with_temperature_profile(t_profile)
}
#[test]
fn test_wetbulb_less_than_dewpoint() {
let snd = create_invalid_test_wetbulb_less_than_dewpoint();
let result = validate(&snd);
let err = result.unwrap_err();
println!("{}", err);
let errs = err.into_inner();
assert!(errs.len() == 3);
for err in errs {
if let ValidationError::WetBulbLessThanDewPoint(wb, dp) = err {
assert!(wb < dp);
} else {
panic!("Error is of wrong type!");
}
}
}
fn create_invalid_test_wetbulb_less_than_dewpoint() -> Sounding {
let wb_profile = vec![
Optioned::from(Celsius(20.0)),
Optioned::from(Celsius(12.0)),
Optioned::from(Celsius(-1.0)),
Optioned::from(Celsius(-13.0)),
Optioned::from(Celsius(-25.0)),
Optioned::from(Celsius(-39.0)),
Optioned::from(Celsius(-58.0)),
Optioned::from(Celsius(-60.0)),
];
create_valid_test_sounding().with_wet_bulb_profile(wb_profile)
}
#[test]
fn test_invalid_negative_value() {
let snd = create_invalid_test_invalid_negative_value();
let result = validate(&snd);
let err = result.unwrap_err();
println!("{}", err);
let errs = err.into_inner();
assert!(errs.len() == 9);
for err in errs {
if let ValidationError::InvalidNegativeValue(_, val) = err {
assert!(val < 0.0);
} else {
assert!(err == ValidationError::PressureNotDecreasingWithHeight);
}
}
}
fn create_invalid_test_invalid_negative_value() -> Sounding {
let wind_speed = vec![
Optioned::from(WindSpdDir {
direction: 0.0,
speed: Knots(-5.0),
}),
Optioned::from(WindSpdDir {
direction: 40.0,
speed: Knots(-10.0),
}),
Optioned::from(WindSpdDir {
direction: 80.0,
speed: Knots(15.0),
}),
Optioned::from(WindSpdDir {
direction: 120.0,
speed: Knots(12.0),
}),
Optioned::from(WindSpdDir {
direction: 160.0,
speed: Knots(27.0),
}),
Optioned::from(WindSpdDir {
direction: 200.0,
speed: Knots(45.0),
}),
Optioned::from(WindSpdDir {
direction: 240.0,
speed: Knots(62.0),
}),
Optioned::from(WindSpdDir {
direction: 280.0,
speed: Knots(80.0),
}),
];
let cc = vec![
Optioned::from(100.0),
Optioned::from(-85.0),
Optioned::from(-70.0),
Optioned::from(50.0),
Optioned::from(30.0),
Optioned::from(25.0),
Optioned::from(20.0),
Optioned::from(10.0),
];
create_valid_test_sounding()
.with_wind_profile(wind_speed)
.with_cloud_fraction_profile(cc)
.with_mslp(HectoPascal(-1014.0))
.with_station_pressure(HectoPascal(-847.0))
.with_sfc_wind(WindSpdDir {
speed: Knots(-10.0),
direction: 100.0,
})
}
#[test]
fn test_invalid_wind_direction() {
let snd = create_invalid_test_wind_direction();
let result = validate(&snd);
let err = result.unwrap_err();
println!("{}", err);
let errs = err.into_inner();
assert_eq!(errs.len(), 4);
for err in errs {
if let ValidationError::InvalidWindDirection(val) = err {
assert!(val < 0.0 || val > 360.0);
} else {
panic!("Error is of wrong type!");
}
}
}
fn create_invalid_test_wind_direction() -> Sounding {
let wind_dir = vec![
Optioned::from(WindSpdDir {
direction: 0.0,
speed: Knots(5.0),
}),
Optioned::from(WindSpdDir {
direction: 40.0,
speed: Knots(10.0),
}),
Optioned::from(WindSpdDir {
direction: -80.0,
speed: Knots(15.0),
}),
Optioned::from(WindSpdDir {
direction: -120.0,
speed: Knots(12.0),
}),
Optioned::from(WindSpdDir {
direction: 160.0,
speed: Knots(27.0),
}),
Optioned::from(WindSpdDir {
direction: 200.0,
speed: Knots(45.0),
}),
Optioned::from(WindSpdDir {
direction: 240.0,
speed: Knots(62.0),
}),
Optioned::from(WindSpdDir {
direction: 280.0,
speed: Knots(80.0),
}),
];
create_valid_test_sounding()
.with_wind_profile(wind_dir)
.with_sfc_wind(WindSpdDir {
direction: -90.0,
speed: Knots(5.0),
})
}