foundation: move apsis-core tests into tests directory
This commit is contained in:
parent
42d2e34bea
commit
d1666ac28a
|
|
@ -134,44 +134,3 @@ pub const STANDARD_ATMOSPHERE: f64 = 101_325.0;
|
||||||
///
|
///
|
||||||
/// Exact by definition of the Celsius scale.
|
/// Exact by definition of the Celsius scale.
|
||||||
pub const ZERO_CELSIUS_IN_KELVIN: f64 = 273.15;
|
pub const ZERO_CELSIUS_IN_KELVIN: f64 = 273.15;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn defining_constants_are_exact() {
|
|
||||||
assert_eq!(SPEED_OF_LIGHT, 299_792_458.0);
|
|
||||||
assert_eq!(BOLTZMANN, 1.380_649e-23);
|
|
||||||
assert_eq!(PLANCK, 6.626_070_15e-34);
|
|
||||||
assert_eq!(ELEMENTARY_CHARGE, 1.602_176_634e-19);
|
|
||||||
assert_eq!(ASTRONOMICAL_UNIT, 149_597_870_700.0);
|
|
||||||
assert_eq!(STANDARD_GRAVITY, 9.806_65);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn time_constants_are_consistent() {
|
|
||||||
assert_eq!(HOUR, MINUTE * 60.0);
|
|
||||||
assert_eq!(DAY, HOUR * 24.0);
|
|
||||||
assert_eq!(JULIAN_YEAR, DAY * 365.25);
|
|
||||||
assert_eq!(JULIAN_CENTURY, JULIAN_YEAR * 100.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn j2000_minus_unix_epoch_is_the_expected_span() {
|
|
||||||
// The two epochs are 10 957 days apart.
|
|
||||||
assert_eq!(JULIAN_DATE_J2000 - JULIAN_DATE_UNIX_EPOCH, 10_957.5);
|
|
||||||
assert_eq!(SECONDS_J2000_TO_UNIX, 10_957.5 * DAY);
|
|
||||||
assert_eq!(SECONDS_J2000_TO_UNIX, 946_728_000.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn earth_flattening_matches_the_inverse_value() {
|
|
||||||
assert!((1.0 / EARTH_FLATTENING - 298.257_223_563).abs() < 1e-9);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn celsius_offset_is_exact() {
|
|
||||||
assert_eq!(ZERO_CELSIUS_IN_KELVIN, 273.15);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -168,78 +168,3 @@ impl From<ErrorKind> for Error {
|
||||||
|
|
||||||
/// A [`Result`](core::result::Result) whose error type is [`Error`].
|
/// A [`Result`](core::result::Result) whose error type is [`Error`].
|
||||||
pub type Result<T> = core::result::Result<T, Error>;
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::{Error, ErrorKind, Result};
|
|
||||||
use core::fmt;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Underlying;
|
|
||||||
|
|
||||||
impl fmt::Display for Underlying {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str("underlying failure")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl core::error::Error for Underlying {}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn kind_is_reported() {
|
|
||||||
let error = Error::new(ErrorKind::Parse);
|
|
||||||
assert_eq!(error.kind(), ErrorKind::Parse);
|
|
||||||
assert_eq!(error.message(), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn message_is_reported() {
|
|
||||||
let error = Error::with_message(ErrorKind::OutOfRange, "semi-major axis is negative");
|
|
||||||
assert_eq!(error.kind(), ErrorKind::OutOfRange);
|
|
||||||
assert_eq!(error.message(), Some("semi-major axis is negative"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn display_includes_kind_and_message() {
|
|
||||||
let error = Error::with_message(ErrorKind::Parse, "bad checksum");
|
|
||||||
assert_eq!(error.to_string(), "parse error: bad checksum");
|
|
||||||
assert_eq!(Error::new(ErrorKind::NotFound).to_string(), "not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn source_is_exposed() {
|
|
||||||
let error = Error::with_source(ErrorKind::Other, Underlying);
|
|
||||||
assert!(std::error::Error::source(&error).is_some());
|
|
||||||
assert_eq!(error.kind(), ErrorKind::Other);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn context_and_cause_replace_previous_values() {
|
|
||||||
let error = Error::new(ErrorKind::Other)
|
|
||||||
.with_context("first")
|
|
||||||
.with_context("second")
|
|
||||||
.caused_by(Underlying);
|
|
||||||
assert_eq!(error.message(), Some("second"));
|
|
||||||
assert!(std::error::Error::source(&error).is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn converts_from_kind() {
|
|
||||||
let error: Error = ErrorKind::Unsupported.into();
|
|
||||||
assert_eq!(error.kind(), ErrorKind::Unsupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn works_in_result_position() {
|
|
||||||
fn fail() -> Result<u32> {
|
|
||||||
Err(Error::new(ErrorKind::NotFinite))
|
|
||||||
}
|
|
||||||
|
|
||||||
let outcome = fail();
|
|
||||||
assert!(outcome.is_err());
|
|
||||||
assert_eq!(
|
|
||||||
outcome.err().map(|error| error.kind()),
|
|
||||||
Some(ErrorKind::NotFinite)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -61,110 +61,3 @@ pub trait Quantity: Copy {
|
||||||
U::from_value(base / <U as Quantity>::U::factor_to_base())
|
U::from_value(base / <U as Quantity>::U::factor_to_base())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::{Quantity, Unit};
|
|
||||||
use core::fmt;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
struct Length;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
struct Meter;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
struct Kilometer;
|
|
||||||
|
|
||||||
impl fmt::Display for Meter {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str("m")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Kilometer {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str("km")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Unit for Meter {
|
|
||||||
type Dimension = Length;
|
|
||||||
|
|
||||||
fn factor_to_base() -> f64 {
|
|
||||||
1.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn symbol() -> &'static str {
|
|
||||||
"m"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Unit for Kilometer {
|
|
||||||
type Dimension = Length;
|
|
||||||
|
|
||||||
fn factor_to_base() -> f64 {
|
|
||||||
1_000.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn symbol() -> &'static str {
|
|
||||||
"km"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
struct Meters(f64);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
struct Kilometers(f64);
|
|
||||||
|
|
||||||
impl Quantity for Meters {
|
|
||||||
type U = Meter;
|
|
||||||
|
|
||||||
fn value(self) -> f64 {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_value(value: f64) -> Self {
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Quantity for Kilometers {
|
|
||||||
type U = Kilometer;
|
|
||||||
|
|
||||||
fn value(self) -> f64 {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_value(value: f64) -> Self {
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn value_round_trips() {
|
|
||||||
let distance = Meters::from_value(1_500.0);
|
|
||||||
assert_eq!(distance.value(), 1_500.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn converts_between_units_of_one_dimension() {
|
|
||||||
let distance = Meters::from_value(2_500.0);
|
|
||||||
let in_kilometers: Kilometers = distance.to_unit();
|
|
||||||
assert!((in_kilometers.value() - 2.5).abs() < 1e-12);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn conversion_round_trips() {
|
|
||||||
let original = Kilometers::from_value(7.25);
|
|
||||||
let round_tripped: Kilometers = original.to_unit::<Meters>().to_unit();
|
|
||||||
assert!((round_tripped.value() - 7.25).abs() < 1e-12);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unit_metadata_is_available() {
|
|
||||||
assert_eq!(<Meter as Unit>::symbol(), "m");
|
|
||||||
assert_eq!(<Kilometer as Unit>::factor_to_base(), 1_000.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
//! Tests for the physical and astronomical constants.
|
||||||
|
|
||||||
|
use apsis_core::constant::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn defining_constants_are_exact() {
|
||||||
|
assert_eq!(SPEED_OF_LIGHT, 299_792_458.0);
|
||||||
|
assert_eq!(BOLTZMANN, 1.380_649e-23);
|
||||||
|
assert_eq!(PLANCK, 6.626_070_15e-34);
|
||||||
|
assert_eq!(ELEMENTARY_CHARGE, 1.602_176_634e-19);
|
||||||
|
assert_eq!(ASTRONOMICAL_UNIT, 149_597_870_700.0);
|
||||||
|
assert_eq!(STANDARD_GRAVITY, 9.806_65);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn time_constants_are_consistent() {
|
||||||
|
assert_eq!(HOUR, MINUTE * 60.0);
|
||||||
|
assert_eq!(DAY, HOUR * 24.0);
|
||||||
|
assert_eq!(JULIAN_YEAR, DAY * 365.25);
|
||||||
|
assert_eq!(JULIAN_CENTURY, JULIAN_YEAR * 100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn j2000_minus_unix_epoch_is_the_expected_span() {
|
||||||
|
// The two epochs are 10 957 days apart.
|
||||||
|
assert_eq!(JULIAN_DATE_J2000 - JULIAN_DATE_UNIX_EPOCH, 10_957.5);
|
||||||
|
assert_eq!(SECONDS_J2000_TO_UNIX, 10_957.5 * DAY);
|
||||||
|
assert_eq!(SECONDS_J2000_TO_UNIX, 946_728_000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn earth_flattening_matches_the_inverse_value() {
|
||||||
|
assert!((1.0 / EARTH_FLATTENING - 298.257_223_563).abs() < 1e-9);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn celsius_offset_is_exact() {
|
||||||
|
assert_eq!(ZERO_CELSIUS_IN_KELVIN, 273.15);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
//! Tests for the workspace error type and result alias.
|
||||||
|
|
||||||
|
use apsis_core::{Error, ErrorKind, Result};
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Underlying;
|
||||||
|
|
||||||
|
impl fmt::Display for Underlying {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("underlying failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::error::Error for Underlying {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn kind_is_reported() {
|
||||||
|
let error = Error::new(ErrorKind::Parse);
|
||||||
|
assert_eq!(error.kind(), ErrorKind::Parse);
|
||||||
|
assert_eq!(error.message(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn message_is_reported() {
|
||||||
|
let error = Error::with_message(ErrorKind::OutOfRange, "semi-major axis is negative");
|
||||||
|
assert_eq!(error.kind(), ErrorKind::OutOfRange);
|
||||||
|
assert_eq!(error.message(), Some("semi-major axis is negative"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_includes_kind_and_message() {
|
||||||
|
let error = Error::with_message(ErrorKind::Parse, "bad checksum");
|
||||||
|
assert_eq!(error.to_string(), "parse error: bad checksum");
|
||||||
|
assert_eq!(Error::new(ErrorKind::NotFound).to_string(), "not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn source_is_exposed() {
|
||||||
|
let error = Error::with_source(ErrorKind::Other, Underlying);
|
||||||
|
assert!(std::error::Error::source(&error).is_some());
|
||||||
|
assert_eq!(error.kind(), ErrorKind::Other);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn context_and_cause_replace_previous_values() {
|
||||||
|
let error = Error::new(ErrorKind::Other)
|
||||||
|
.with_context("first")
|
||||||
|
.with_context("second")
|
||||||
|
.caused_by(Underlying);
|
||||||
|
assert_eq!(error.message(), Some("second"));
|
||||||
|
assert!(std::error::Error::source(&error).is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn converts_from_kind() {
|
||||||
|
let error: Error = ErrorKind::Unsupported.into();
|
||||||
|
assert_eq!(error.kind(), ErrorKind::Unsupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn works_in_result_position() {
|
||||||
|
fn fail() -> Result<u32> {
|
||||||
|
Err(Error::new(ErrorKind::NotFinite))
|
||||||
|
}
|
||||||
|
|
||||||
|
let outcome = fail();
|
||||||
|
assert!(outcome.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
outcome.err().map(|error| error.kind()),
|
||||||
|
Some(ErrorKind::NotFinite)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
//! Tests for the unit and quantity marker traits.
|
||||||
|
|
||||||
|
use apsis_core::{Quantity, Unit};
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
struct Length;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct Meter;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct Kilometer;
|
||||||
|
|
||||||
|
impl fmt::Display for Meter {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("m")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Kilometer {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("km")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Unit for Meter {
|
||||||
|
type Dimension = Length;
|
||||||
|
|
||||||
|
fn factor_to_base() -> f64 {
|
||||||
|
1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn symbol() -> &'static str {
|
||||||
|
"m"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Unit for Kilometer {
|
||||||
|
type Dimension = Length;
|
||||||
|
|
||||||
|
fn factor_to_base() -> f64 {
|
||||||
|
1_000.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn symbol() -> &'static str {
|
||||||
|
"km"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
struct Meters(f64);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
struct Kilometers(f64);
|
||||||
|
|
||||||
|
impl Quantity for Meters {
|
||||||
|
type U = Meter;
|
||||||
|
|
||||||
|
fn value(self) -> f64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_value(value: f64) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Quantity for Kilometers {
|
||||||
|
type U = Kilometer;
|
||||||
|
|
||||||
|
fn value(self) -> f64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_value(value: f64) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn value_round_trips() {
|
||||||
|
let distance = Meters::from_value(1_500.0);
|
||||||
|
assert_eq!(distance.value(), 1_500.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn converts_between_units_of_one_dimension() {
|
||||||
|
let distance = Meters::from_value(2_500.0);
|
||||||
|
let in_kilometers: Kilometers = distance.to_unit();
|
||||||
|
assert!((in_kilometers.value() - 2.5).abs() < 1e-12);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn conversion_round_trips() {
|
||||||
|
let original = Kilometers::from_value(7.25);
|
||||||
|
let round_tripped: Kilometers = original.to_unit::<Meters>().to_unit();
|
||||||
|
assert!((round_tripped.value() - 7.25).abs() < 1e-12);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unit_metadata_is_available() {
|
||||||
|
assert_eq!(<Meter as Unit>::symbol(), "m");
|
||||||
|
assert_eq!(<Kilometer as Unit>::factor_to_base(), 1_000.0);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue