#![cfg(feature = "lock")]
#[macro_use]
extern crate approx;
#[macro_use]
extern crate serial_test;
#[path = "../common/mod.rs"]
mod common;
use spice::SpiceLock;
fn acquire() -> SpiceLock {
common::reset();
let lock = SpiceLock::try_acquire().expect("no other lock should be held");
lock.quiet();
lock.furnsh(&common::meta());
common::assert_ok("loading the test meta-kernel");
lock
}
#[test]
#[serial]
fn only_one_lock_at_a_time() {
let lock = SpiceLock::try_acquire().unwrap();
assert!(SpiceLock::try_acquire().is_err());
drop(lock);
let lock = SpiceLock::try_acquire();
assert!(lock.is_ok());
drop(lock);
assert!(SpiceLock::try_acquire().is_ok());
}
#[test]
#[serial]
fn raw_functions_are_exposed() {
let sl = acquire();
let et = sl.str2et("2000-JAN-02 00:00:00 TDB");
assert_relative_eq!(et, 43200.0, epsilon = 1e-9);
let (position, light_time) = sl.spkpos("EARTH", et, "J2000", "NONE", "SUN");
common::assert_ok("spkpos");
for (component, expected) in position.iter().zip(common::analytic_state(et).iter()) {
assert_relative_eq!(component, expected, epsilon = 1e-3);
}
assert_relative_eq!(
light_time,
sl.vnorm(position) / sl.clight(),
epsilon = 1e-12
);
assert_eq!(sl.bodn2c("EARTH"), (399, true));
assert!(sl.bodfnd(399, "RADII"));
assert_relative_eq!(
sl.vdot([1.0, 2.0, 3.0], [1.0, 2.0, 3.0]),
14.0,
epsilon = f64::EPSILON
);
sl.kclear();
}
#[test]
#[serial]
fn neat_functions_are_preferred() {
let sl = acquire();
assert_eq!(sl.bodc2n(399), ("EARTH".to_string(), true));
assert_eq!(sl.timout(0.0, spice::TIME_FORMAT), "2000-JAN-01 11:58:56");
let bodies = sl.dskobj(&common::kernel("test.bds"));
common::assert_ok("dskobj");
assert_eq!(bodies.to_vec(), vec![common::TARGET]);
let coverage = sl.spkcov(&common::kernel("test.bsp"), common::TARGET);
assert_eq!(coverage.len(), 2);
assert_relative_eq!(coverage.get(0).unwrap(), common::FIRST, epsilon = 1e-6);
sl.kclear();
}
#[test]
#[serial]
fn errors_are_reachable() {
let sl = acquire();
assert!(!sl.failed());
assert!(sl.check().is_ok());
sl.furnsh("no-such-kernel.tm");
assert!(sl.failed());
assert_eq!(sl.getmsg("SHORT"), "SPICE(NOSUCHFILE)");
let error = sl.check().expect_err("furnsh should have failed");
assert_eq!(error.short, "SPICE(NOSUCHFILE)");
assert!(!sl.failed(), "`check` resets the state");
assert_eq!(sl.erract("GET", ""), "RETURN");
sl.reset();
sl.kclear();
}
#[test]
#[serial]
fn the_lock_can_be_shared_between_threads() {
use std::sync::{Arc, Mutex};
use std::thread;
let sl = Arc::new(Mutex::new(acquire()));
let children = (0..5)
.map(|_| {
let sl = Arc::clone(&sl);
thread::spawn(move || {
(0..10)
.map(|_| {
let sl = sl.lock().unwrap();
sl.str2et("2000-JAN-02 00:00:00 TDB")
})
.collect::<Vec<_>>()
})
})
.collect::<Vec<_>>();
for child in children {
for value in child.join().unwrap() {
assert_relative_eq!(value, 43200.0, epsilon = 1e-9);
}
}
sl.lock().unwrap().kclear();
}