use std::path::PathBuf;
use serde_json::Value;
use super::{solid_earth_tide, solid_earth_tide_unchecked, TideError, TideInputErrorKind};
fn fixture_path() -> PathBuf {
let crate_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
crate_dir
.join("tests/fixtures/tides/tides_dehant_golden.json")
.canonicalize()
.unwrap_or_else(|e| {
panic!(
"cannot locate tests/fixtures/tides/tides_dehant_golden.json from {}: {e}",
crate_dir.display()
)
})
}
fn vec3(v: &Value) -> [f64; 3] {
let a = v["values"].as_array().expect("values array");
[
a[0].as_f64().unwrap(),
a[1].as_f64().unwrap(),
a[2].as_f64().unwrap(),
]
}
#[test]
fn solid_earth_tide_matches_iers_dehant_golden() {
let raw = std::fs::read_to_string(fixture_path()).expect("read tides_dehant_golden.json");
let doc: Value = serde_json::from_str(&raw).expect("parse tides_dehant_golden.json");
let cases = doc["cases"].as_array().expect("cases array");
const TOL_M: f64 = 1.0e-9;
let mut failures: Vec<String> = Vec::new();
let mut max_dev = 0.0_f64;
for case in cases {
let id = case["id"].as_str().unwrap_or("?");
if id == "case_4_2017_01_15" {
continue;
}
let inputs = &case["inputs"];
let xsta = vec3(&inputs["xsta_m"]);
let xsun = vec3(&inputs["xsun_m"]);
let xmon = vec3(&inputs["xmon_m"]);
let year = inputs["date_utc"]["year"].as_i64().unwrap() as i32;
let month = inputs["date_utc"]["month"].as_i64().unwrap() as i32;
let day = inputs["date_utc"]["day"].as_i64().unwrap() as i32;
let fhr = inputs["fhr_hours"]["value"].as_f64().unwrap();
let expected = vec3(&case["expected"]["dxtide_m"]);
let got =
solid_earth_tide(&xsta, year, month, day, fhr, &xsun, &xmon).expect("valid tide input");
for k in 0..3 {
let dev = (got[k] - expected[k]).abs();
if dev > max_dev {
max_dev = dev;
}
if dev > TOL_M {
failures.push(format!(
"{id} component {k}: got {:.18e}, expected {:.18e}, dev {:.3e} m",
got[k], expected[k], dev
));
}
}
}
assert!(
failures.is_empty(),
"solid-earth tide golden mismatch (max dev {max_dev:.3e} m):\n{}",
failures.join("\n")
);
}
fn assert_invalid_input(
got: Result<[f64; 3], TideError>,
field: &'static str,
kind: TideInputErrorKind,
) {
assert_eq!(
got.expect_err("invalid tide input must error"),
TideError::InvalidInput { field, kind }
);
}
fn valid_tide_inputs() -> ([f64; 3], [f64; 3], [f64; 3]) {
(
[3_512_900.0, 780_500.0, 5_248_700.0],
[
1.379_133_792_566_993e11,
-5.521_095_241_319_248e10,
-2.394_349_831_958_611e10,
],
[
1.749_761_742_158_154e8,
-3.202_053_263_558_994e8,
-1.746_291_411_625_388e8,
],
)
}
#[test]
fn solid_earth_tide_rejects_degenerate_geometry() {
let (station, sun, moon) = valid_tide_inputs();
assert_invalid_input(
solid_earth_tide(&[0.0, 0.0, 0.0], 2020, 6, 24, 12.0, &sun, &moon),
"station radius",
TideInputErrorKind::NotPositive,
);
assert_invalid_input(
solid_earth_tide(&[0.0, 0.0, 6_378_136.6], 2020, 6, 24, 12.0, &sun, &moon),
"station horizontal radius",
TideInputErrorKind::NotPositive,
);
assert_invalid_input(
solid_earth_tide(&station, 2020, 6, 24, 12.0, &[0.0, 0.0, 0.0], &moon),
"sun radius",
TideInputErrorKind::NotPositive,
);
assert_invalid_input(
solid_earth_tide(&station, 2020, 6, 24, 12.0, &sun, &[0.0, 0.0, 0.0]),
"moon radius",
TideInputErrorKind::NotPositive,
);
}
#[test]
fn solid_earth_tide_rejects_invalid_civil_date_and_hour() {
let (station, sun, moon) = valid_tide_inputs();
assert_invalid_input(
solid_earth_tide(&station, 2020, 13, 24, 12.0, &sun, &moon),
"civil datetime",
TideInputErrorKind::InvalidCivilDate,
);
assert_invalid_input(
solid_earth_tide(&station, 2021, 2, 31, 12.0, &sun, &moon),
"civil datetime",
TideInputErrorKind::InvalidCivilDate,
);
assert_invalid_input(
solid_earth_tide(&station, 2020, 6, 24, 24.0, &sun, &moon),
"fractional hour",
TideInputErrorKind::OutOfRange,
);
assert_invalid_input(
solid_earth_tide(&station, 2020, 6, 24, -0.25, &sun, &moon),
"fractional hour",
TideInputErrorKind::OutOfRange,
);
}
#[test]
fn solid_earth_tide_valid_date_and_hour_matches_unchecked_result() {
let (station, sun, moon) = valid_tide_inputs();
let got = solid_earth_tide(&station, 2020, 6, 24, 23.5, &sun, &moon).expect("valid tide input");
let expected = solid_earth_tide_unchecked(&station, 2020, 6, 24, 23.5, &sun, &moon);
assert_eq!(got, expected);
}