8 Commits
Author SHA1 Message Date
swaits b1c37b7b8f bump crate to 0.6.2
Signed-off-by: Stephen Waits <steve@waits.net>
2023-02-13 10:58:18 -07:00
swaits bc043ad54b fix(test): allow clippy lints on specific tests 2023-02-13 10:51:28 -07:00
swaits 96f64f189c dev: add husky for pre-commit hook generation
Signed-off-by: Stephen Waits <steve@waits.net>
2023-02-13 10:48:19 -07:00
swaits 609cc3e98c refactor: clean up by using cfg-if where possible
Signed-off-by: Stephen Waits <steve@waits.net>
2023-02-13 10:41:49 -07:00
swaits d1bc523b67 misc cleanup
- move from std::ops to core::ops
- remove unnecessary println! calls in tests

Signed-off-by: Stephen Waits <steve@waits.net>
2023-02-13 09:52:35 -07:00
swaits e6011b4548 refactor: change unwrap()s to expects() and deny in clippy
Signed-off-by: Stephen Waits <steve@waits.net>
2023-02-13 09:30:00 -07:00
swaits c267d4b9b2 refactor: change std::time::Duration to std::core::Duration
One step closer to `no_std`.

Signed-off-by: Stephen Waits <steve@waits.net>
2023-02-13 09:29:12 -07:00
swaits b76c6c9775 test: use prop_assert* macros where appropriate
The prop_assert* macros clean-up the assertion output by eliminating the extra panics while it’s reducing inputs.

Signed-off-by: Stephen Waits <steve@waits.net>
2023-02-13 09:28:09 -07:00
9 changed files with 122 additions and 74 deletions

No files matched your search

+7 -7
View File
@@ -14,11 +14,11 @@ tasks:
- format: | - format: |
rustup default stable rustup default stable
cd vivaldi-nc/ cd vivaldi-nc/
cargo fmt --verbose --check cargo fmt --verbose --check --all
- lint: | - lint: |
rustup default stable rustup default stable
cd vivaldi-nc/ cd vivaldi-nc/
cargo clippy --verbose cargo clippy --verbose --all-targets --all-features
- coverage: | - coverage: |
rustup default stable rustup default stable
cd vivaldi-nc/ cd vivaldi-nc/
@@ -26,18 +26,18 @@ tasks:
- stable: | - stable: |
rustup default stable rustup default stable
cd vivaldi-nc/ cd vivaldi-nc/
cargo build --verbose cargo build --verbose --all-targets --all-features
cargo test --verbose --all-features cargo test --verbose --all-features
- nightly: | - nightly: |
rustup default nightly rustup default nightly
cd vivaldi-nc/ cd vivaldi-nc/
cargo clippy --verbose ||: cargo clippy --verbose --all-targets --all-features ||:
cargo build --verbose ||: cargo build --verbose --all-targets --all-features ||:
cargo test --verbose --all-features ||: cargo test --verbose --all-targets --all-features ||:
- bench: | - bench: |
rustup default stable rustup default stable
cd vivaldi-nc/ cd vivaldi-nc/
cargo bench --verbose --all-features cargo bench --verbose --all-targets --all-features
- examples: | - examples: |
rustup default stable rustup default stable
cd vivaldi-nc/ cd vivaldi-nc/
+3 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "vivaldi-nc" name = "vivaldi-nc"
version = "0.6.1" version = "0.6.2"
edition = "2021" edition = "2021"
authors = ["Stephen Waits <steve@waits.net>"] authors = ["Stephen Waits <steve@waits.net>"]
description = "Vivaldi network coordinates for fast, distributed latency estimates in multinode networks, with a clean and simple interface." description = "Vivaldi network coordinates for fast, distributed latency estimates in multinode networks, with a clean and simple interface."
@@ -28,6 +28,7 @@ debug = true
[dev-dependencies] [dev-dependencies]
assert_approx_eq = "1.1.0" assert_approx_eq = "1.1.0"
criterion = "0.4.0" criterion = "0.4.0"
cargo-husky = { version = "1.5.0", features = ["run-for-all", "precommit-hook", "run-cargo-test", "run-cargo-check", "run-cargo-clippy", "run-cargo-fmt"] }
proptest = "1.0.0" proptest = "1.0.0"
serde_json = "1.0.91" serde_json = "1.0.91"
@@ -37,6 +38,7 @@ harness = false
[dependencies] [dependencies]
array-init = "2.1.0" array-init = "2.1.0"
cfg-if = "1.0.0"
num-traits = "0.2.15" num-traits = "0.2.15"
rand = "0.8.5" rand = "0.8.5"
serde = {version = "1.0.152", features = ["serde_derive"]} serde = {version = "1.0.152", features = ["serde_derive"]}
+2
View File
@@ -192,6 +192,8 @@ This crate depends on:
operations for the internal `Vector` so much nicer. Once we have operations for the internal `Vector` so much nicer. Once we have
`feature(array_zip)` stabilized, we could use that instead. Until then, this `feature(array_zip)` stabilized, we could use that instead. Until then, this
works well with zero performance impact. works well with zero performance impact.
- [`cfg-if`](https://crates.io/crates/cfg-if): Compile-time only macro which
makes using `cfg` parameters much nicer in code.
## Design Goals & Alternatives ## Design Goals & Alternatives
+1 -1
View File
@@ -1,4 +1,4 @@
use std::time::Duration; use core::time::Duration;
use criterion::{criterion_group, criterion_main, Criterion}; use criterion::{criterion_group, criterion_main, Criterion};
use vivaldi_nc::{NetworkCoordinate2D, NetworkCoordinate3D}; use vivaldi_nc::{NetworkCoordinate2D, NetworkCoordinate3D};
+32 -23
View File
@@ -53,13 +53,15 @@ use crate::vector::{self, Vector};
// **** Features **** // **** Features ****
// //
/// `FloatType` is a type alias for either `f32` or `f64` depending on cargo features cfg_if::cfg_if! {
#[cfg(feature = "f32")] if #[cfg(feature = "f32")] {
type FloatType = f32; /// `FloatType` is a type alias for either `f32` or `f64` depending on cargo features
type FloatType = f32;
/// `FloatType` is a type alias for either `f32` or `f64` depending on cargo features } else {
#[cfg(not(feature = "f32"))] /// `FloatType` is a type alias for either `f32` or `f64` depending on cargo features
type FloatType = f64; type FloatType = f64;
}
}
// //
// **** Constants **** // **** Constants ****
@@ -240,26 +242,30 @@ mod tests {
use assert_approx_eq::assert_approx_eq; use assert_approx_eq::assert_approx_eq;
use proptest::prelude::*; use proptest::prelude::*;
fn approx_eq(a: FloatType, b: FloatType, margin: FloatType) -> bool {
(a - b).abs() <= margin
}
proptest! { proptest! {
#[test] #[test]
fn proptest_len(x: FloatType, y: FloatType, h: FloatType) { fn proptest_len(x: FloatType, y: FloatType, h: FloatType) {
let len = x.hypot(y) + h.abs(); let len = x.hypot(y) + h.abs();
let a = HeightVector::<2>::from(([x,y],h)); let a = HeightVector::<2>::from(([x,y],h));
println!("a = {a:#?}");
if x.is_nan() || x.is_infinite() || y.is_nan() || y.is_infinite() || h.is_nan() || h.is_infinite() || h < 0.0 { if x.is_nan() || x.is_infinite() || y.is_nan() || y.is_infinite() || h.is_nan() || h.is_infinite() || h < 0.0 {
// we should've gottne a random univ vector here // we should've gottne a random univ vector here
assert_approx_eq!(a.len(), 1.0); prop_assert!(approx_eq(a.len(), 1.0, 0.0001));
} else { } else {
// `HeightVector` we created from proptest values should be valid and have a length // `HeightVector` we created from proptest values should be valid and have a length
if len.is_nan() || len.is_infinite() { if len.is_nan() || len.is_infinite() {
assert!(a.len().is_nan() || a.len().is_infinite()); prop_assert!(a.len().is_nan() || a.len().is_infinite());
} else { } else {
assert_approx_eq!(a.len(), len); prop_assert!(approx_eq(a.len(), len, 0.0001));
} }
} }
} }
#[test] #[test]
#[allow(clippy::cast_lossless, clippy::cast_precision_loss)]
fn proptest_add(x0 in -1_000_000_000..1_000_000_000i32, y0 in -1_000_000_000..1_000_000_000i32, h0 in 0..1_000_000_000i32, x1 in -1_000_000_000..1_000_000_000i32, y1 in -1_000_000_000..1_000_000_000i32, h1 in 0..1_000_000i32) { fn proptest_add(x0 in -1_000_000_000..1_000_000_000i32, y0 in -1_000_000_000..1_000_000_000i32, h0 in 0..1_000_000_000i32, x1 in -1_000_000_000..1_000_000_000i32, y1 in -1_000_000_000..1_000_000_000i32, h1 in 0..1_000_000i32) {
// convert our integer range inputs to FloatType // convert our integer range inputs to FloatType
let (fx0,fy0,fh0) = (x0 as FloatType / 1_000.0, y0 as FloatType / 1_000.0, h0 as FloatType / 1_000.0); let (fx0,fy0,fh0) = (x0 as FloatType / 1_000.0, y0 as FloatType / 1_000.0, h0 as FloatType / 1_000.0);
@@ -268,12 +274,13 @@ mod tests {
let a = HeightVector::<2>::from(([fx0 ,fy0 ],fh0 )); let a = HeightVector::<2>::from(([fx0 ,fy0 ],fh0 ));
let b = HeightVector::<2>::from(([fx1 ,fy1 ],fh1 )); let b = HeightVector::<2>::from(([fx1 ,fy1 ],fh1 ));
let c = a+b; let c = a+b;
assert_approx_eq!(c.position[0], fx0 + fx1); prop_assert!(approx_eq(c.position[0], fx0 + fx1, 0.0001));
assert_approx_eq!(c.position[1], fy0 + fy1); prop_assert!(approx_eq(c.position[1], fy0 + fy1, 0.0001));
assert_approx_eq!(c.height, fh0 + fh1); prop_assert!(approx_eq(c.height, fh0 + fh1, 0.0001));
} }
#[test] #[test]
#[allow(clippy::cast_lossless, clippy::cast_precision_loss)]
fn proptest_sub(x0 in -1_000_000_000..1_000_000_000i32, y0 in -1_000_000_000..1_000_000_000i32, h0 in 0..1_000_000_000i32, x1 in -1_000_000_000..1_000_000_000i32, y1 in -1_000_000_000..1_000_000_000i32, h1 in 0..1_000_000i32) { fn proptest_sub(x0 in -1_000_000_000..1_000_000_000i32, y0 in -1_000_000_000..1_000_000_000i32, h0 in 0..1_000_000_000i32, x1 in -1_000_000_000..1_000_000_000i32, y1 in -1_000_000_000..1_000_000_000i32, h1 in 0..1_000_000i32) {
// convert our integer range inputs to FloatType // convert our integer range inputs to FloatType
let (fx0,fy0,fh0) = (x0 as FloatType / 1_000.0, y0 as FloatType / 1_000.0, h0 as FloatType / 1_000.0); let (fx0,fy0,fh0) = (x0 as FloatType / 1_000.0, y0 as FloatType / 1_000.0, h0 as FloatType / 1_000.0);
@@ -282,12 +289,13 @@ mod tests {
let a = HeightVector::<2>::from(([fx0 ,fy0 ],fh0 )); let a = HeightVector::<2>::from(([fx0 ,fy0 ],fh0 ));
let b = HeightVector::<2>::from(([fx1 ,fy1 ],fh1 )); let b = HeightVector::<2>::from(([fx1 ,fy1 ],fh1 ));
let c = a-b; let c = a-b;
assert_approx_eq!(c.position[0], fx0 - fx1); prop_assert!(approx_eq(c.position[0], fx0 - fx1, 0.0001));
assert_approx_eq!(c.position[1], fy0 - fy1); prop_assert!(approx_eq(c.position[1], fy0 - fy1, 0.0001));
assert_approx_eq!(c.height, fh0 + fh1); prop_assert!(approx_eq(c.height, fh0 + fh1, 0.0001));
} }
#[test] #[test]
#[allow(clippy::cast_lossless, clippy::cast_precision_loss)]
fn proptest_mul(x in -1_000_000_000..1_000_000_000i32, y in -1_000_000_000..1_000_000_000i32, h in 0..1_000_000_000i32, m in -1_000_000_000..1_000_000_000i32) { fn proptest_mul(x in -1_000_000_000..1_000_000_000i32, y in -1_000_000_000..1_000_000_000i32, h in 0..1_000_000_000i32, m in -1_000_000_000..1_000_000_000i32) {
// convert our integer range inputs to FloatType // convert our integer range inputs to FloatType
let (fx,fy,fh,fm) = (x as FloatType / 1_000.0, y as FloatType / 1_000.0, h as FloatType / 1_000.0, m as FloatType / 1_000.0); let (fx,fy,fh,fm) = (x as FloatType / 1_000.0, y as FloatType / 1_000.0, h as FloatType / 1_000.0, m as FloatType / 1_000.0);
@@ -295,11 +303,11 @@ mod tests {
let a = HeightVector::<2>::from(([fx, fy], fh)); let a = HeightVector::<2>::from(([fx, fy], fh));
let b = a * fm; let b = a * fm;
if fm < 0.0 { if fm < 0.0 {
assert_approx_eq!(b.len(), 1.0); prop_assert!(approx_eq(b.len(), 1.0, 0.0001));
} else { } else {
assert_approx_eq!(b.position[0], fx * fm); prop_assert!(approx_eq(b.position[0], fx * fm, 0.0001));
assert_approx_eq!(b.position[1], fy * fm); prop_assert!(approx_eq(b.position[1], fy * fm, 0.0001));
assert_approx_eq!(b.height, fh * fm); prop_assert!(approx_eq(b.height, fh * fm, 0.0001));
} }
} }
} }
@@ -364,14 +372,15 @@ mod tests {
fn test_serde() { fn test_serde() {
// start with JSON, deserialize it // start with JSON, deserialize it
let s = "{\"position\":[1.0,2.0,3.0],\"height\":4.0}"; let s = "{\"position\":[1.0,2.0,3.0],\"height\":4.0}";
let a: HeightVector<3> = serde_json::from_str(s).unwrap(); let a: HeightVector<3> =
serde_json::from_str(s).expect("deserialization failed during test");
// make sure it's the right length and works like we expect a normal NC // make sure it's the right length and works like we expect a normal NC
assert_approx_eq!(a.len(), 7.741_657, 0.001); assert_approx_eq!(a.len(), 7.741_657, 0.001);
// serialize it into a new JSON string and make sure it matches the original // serialize it into a new JSON string and make sure it matches the original
let t = serde_json::to_string(&a); let t = serde_json::to_string(&a);
assert_eq!(t.as_ref().unwrap(), s); assert_eq!(t.as_ref().expect("serialization failed during test"), s);
} }
#[test] #[test]
+1
View File
@@ -102,6 +102,7 @@
#![deny(clippy::cargo)] #![deny(clippy::cargo)]
#![deny(clippy::pedantic)] #![deny(clippy::pedantic)]
#![deny(clippy::nursery)] #![deny(clippy::nursery)]
#![deny(clippy::unwrap_used)]
#![allow(clippy::type_repetition_in_bounds)] #![allow(clippy::type_repetition_in_bounds)]
#![allow(clippy::module_name_repetitions)] #![allow(clippy::module_name_repetitions)]
+27 -22
View File
@@ -2,7 +2,7 @@
//! //!
//! For usage explanation and examples, please see the main [`crate`] documentation. //! For usage explanation and examples, please see the main [`crate`] documentation.
use std::time::Duration; use core::time::Duration;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -124,11 +124,13 @@ impl<const N: usize> NetworkCoordinate<N> {
#[must_use] #[must_use]
pub fn estimated_rtt(&self, rhs: &Self) -> Duration { pub fn estimated_rtt(&self, rhs: &Self) -> Duration {
// estimated rss is euclidean distance between the two plus the sum of the heights // estimated rss is euclidean distance between the two plus the sum of the heights
#[cfg(feature = "f32")] cfg_if::cfg_if! {
return Duration::from_secs_f32((self.heightvec - rhs.heightvec).len() / 1000.0); if #[cfg(feature = "f32")] {
Duration::from_secs_f32((self.heightvec - rhs.heightvec).len() / 1000.0)
#[cfg(not(feature = "f32"))] } else {
return Duration::from_secs_f64((self.heightvec - rhs.heightvec).len() / 1000.0); Duration::from_secs_f64((self.heightvec - rhs.heightvec).len() / 1000.0)
}
}
} }
/// Given another Vivaldi [`NetworkCoordinate`], adjust our coordinateto better represent the actual round /// Given another Vivaldi [`NetworkCoordinate`], adjust our coordinateto better represent the actual round
@@ -146,7 +148,7 @@ impl<const N: usize> NetworkCoordinate<N> {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// use std::time::Duration; /// use core::time::Duration;
/// use vivaldi_nc::NetworkCoordinate; /// use vivaldi_nc::NetworkCoordinate;
/// ///
/// // We always have our own NC: /// // We always have our own NC:
@@ -190,15 +192,15 @@ impl<const N: usize> NetworkCoordinate<N> {
/// ///
pub fn update(&mut self, rhs: &Self, rtt: Duration) -> &Self { pub fn update(&mut self, rhs: &Self, rtt: Duration) -> &Self {
// convert Durations into FloatType as fractional milliseconds for convenience // convert Durations into FloatType as fractional milliseconds for convenience
#[cfg(feature = "f32")] cfg_if::cfg_if! {
let rtt_ms = rtt.as_secs_f32() * 1000.0; if #[cfg(feature = "f32")] {
#[cfg(feature = "f32")] let rtt_ms = rtt.as_secs_f32() * 1000.0;
let rtt_estimated_ms = self.estimated_rtt(rhs).as_secs_f32() * 1000.0; let rtt_estimated_ms = self.estimated_rtt(rhs).as_secs_f32() * 1000.0;
} else {
#[cfg(not(feature = "f32"))] let rtt_ms = rtt.as_secs_f64() * 1000.0;
let rtt_ms = rtt.as_secs_f64() * 1000.0; let rtt_estimated_ms = self.estimated_rtt(rhs).as_secs_f64() * 1000.0;
#[cfg(not(feature = "f32"))] }
let rtt_estimated_ms = self.estimated_rtt(rhs).as_secs_f64() * 1000.0; }
// rtt needs to be positive // rtt needs to be positive
if rtt_ms < 0.0 { if rtt_ms < 0.0 {
@@ -352,7 +354,6 @@ mod tests {
// compute and test the root mean squared error // compute and test the root mean squared error
let error = slc.error + nyc.error + lax.error + mad.error; let error = slc.error + nyc.error + lax.error + mad.error;
println!("error = {error}");
assert!(error < 5.0); assert!(error < 5.0);
} }
@@ -360,7 +361,8 @@ mod tests {
fn test_serde() { fn test_serde() {
// start with JSON, deserialize it // start with JSON, deserialize it
let s = "{\"position\":[1.5,0.5,2.0],\"height\":0.1,\"error\":1.0}"; let s = "{\"position\":[1.5,0.5,2.0],\"height\":0.1,\"error\":1.0}";
let a: NetworkCoordinate<3> = serde_json::from_str(s).unwrap(); let a: NetworkCoordinate<3> =
serde_json::from_str(s).expect("deserialization failed during test");
// make sure it's the right length and works like we expect a normal NC // make sure it's the right length and works like we expect a normal NC
assert_approx_eq!(a.heightvec.len(), 2.649_509, 0.001); assert_approx_eq!(a.heightvec.len(), 2.649_509, 0.001);
@@ -369,16 +371,18 @@ mod tests {
// serialize it into a new JSON string and make sure it matches the original // serialize it into a new JSON string and make sure it matches the original
let t = serde_json::to_string(&a); let t = serde_json::to_string(&a);
assert_eq!(t.as_ref().unwrap(), s); assert_eq!(t.as_ref().expect("serialization failed during test"), s);
} }
#[test] #[test]
fn test_estimated_rtt() { fn test_estimated_rtt() {
// start with JSON, deserialize it // start with JSON, deserialize it
let s = "{\"position\":[1.5,0.5,2.0],\"height\":25.0,\"error\":1.0}"; let s = "{\"position\":[1.5,0.5,2.0],\"height\":25.0,\"error\":1.0}";
let a: NetworkCoordinate<3> = serde_json::from_str(s).unwrap(); let a: NetworkCoordinate<3> =
serde_json::from_str(s).expect("deserialization failed during test");
let s = "{\"position\":[-1.5,-0.5,-2.0],\"height\":50.0,\"error\":1.0}"; let s = "{\"position\":[-1.5,-0.5,-2.0],\"height\":50.0,\"error\":1.0}";
let b: NetworkCoordinate<3> = serde_json::from_str(s).unwrap(); let b: NetworkCoordinate<3> =
serde_json::from_str(s).expect("deserialization failed during test");
let estimate = a.estimated_rtt(&b); let estimate = a.estimated_rtt(&b);
assert_approx_eq!(estimate.as_secs_f32(), 0.080_099); assert_approx_eq!(estimate.as_secs_f32(), 0.080_099);
@@ -387,7 +391,8 @@ mod tests {
#[test] #[test]
fn test_error_getter() { fn test_error_getter() {
let s = "{\"position\":[1.5,0.5,2.0],\"height\":25.0,\"error\":1.0}"; let s = "{\"position\":[1.5,0.5,2.0],\"height\":25.0,\"error\":1.0}";
let a: NetworkCoordinate<3> = serde_json::from_str(s).unwrap(); let a: NetworkCoordinate<3> =
serde_json::from_str(s).expect("deserialization failed during test");
assert_approx_eq!(a.error(), 1.0); assert_approx_eq!(a.error(), 1.0);
} }
} }
+27 -20
View File
@@ -8,7 +8,7 @@ use num_traits::Float;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::serde_as; use serde_with::serde_as;
use std::ops::{Add, Div, Index, Mul, Sub}; use core::ops::{Add, Div, Index, Mul, Sub};
// //
// **** Structs **** // **** Structs ****
@@ -157,16 +157,20 @@ mod tests {
use assert_approx_eq::assert_approx_eq; use assert_approx_eq::assert_approx_eq;
use proptest::prelude::*; use proptest::prelude::*;
fn approx_eq(a: f32, b: f32, margin: f32) -> bool {
(a - b).abs() <= margin
}
proptest! { proptest! {
#[test] #[test]
fn proptest_new(x: f32, y: f32, z: f32){ fn proptest_new(x: f32, y: f32, z: f32){
let v = Vector::<f32, 3>::from([x,y,z]); let v = Vector::<f32, 3>::from([x,y,z]);
assert_approx_eq!(v[0], x); prop_assert!(approx_eq(v[0], x, 0.0001));
assert_approx_eq!(v[1], y); prop_assert!(approx_eq(v[1], y, 0.0001));
assert_approx_eq!(v[2], z); prop_assert!(approx_eq(v[2], z, 0.0001));
assert!(v[0].is_finite()); prop_assert!(v[0].is_finite());
assert!(v[1].is_finite()); prop_assert!(v[1].is_finite());
assert!(v[1].is_finite()); prop_assert!(v[1].is_finite());
} }
#[test] #[test]
@@ -176,21 +180,21 @@ mod tests {
let (x,y,z) = (v[0]*m, v[1]*m, v[2]*m); let (x,y,z) = (v[0]*m, v[1]*m, v[2]*m);
if x.is_nan() || x.is_infinite() { if x.is_nan() || x.is_infinite() {
assert!(w[0].is_nan() || w[0].is_infinite()); prop_assert!(w[0].is_nan() || w[0].is_infinite());
} else { } else {
assert_approx_eq!(w[0], x); prop_assert!(approx_eq(w[0], x, 0.0001));
} }
if y.is_nan() || y.is_infinite() { if y.is_nan() || y.is_infinite() {
assert!(w[1].is_nan() || w[1].is_infinite()); prop_assert!(w[1].is_nan() || w[1].is_infinite());
} else { } else {
assert_approx_eq!(w[1], y); prop_assert!(approx_eq(w[1], y, 0.0001));
} }
if z.is_nan() || z.is_infinite() { if z.is_nan() || z.is_infinite() {
assert!(w[2].is_nan() || w[2].is_infinite()); prop_assert!(w[2].is_nan() || w[2].is_infinite());
} else { } else {
assert_approx_eq!(w[2], z); prop_assert!(approx_eq(w[2], z, 0.0001));
} }
} }
@@ -201,21 +205,21 @@ mod tests {
let (x,y,z) = (v[0]/d, v[1]/d, v[2]/d); let (x,y,z) = (v[0]/d, v[1]/d, v[2]/d);
if x.is_nan() || x.is_infinite() { if x.is_nan() || x.is_infinite() {
assert!(w[0].is_nan() ||w[0].is_infinite()); prop_assert!(w[0].is_nan() ||w[0].is_infinite());
} else { } else {
assert_approx_eq!(w[0], x); prop_assert!(approx_eq(w[0], x, 0.0001));
} }
if y.is_nan() || y.is_infinite() { if y.is_nan() || y.is_infinite() {
assert!(w[1].is_nan() || w[1].is_infinite()); prop_assert!(w[1].is_nan() || w[1].is_infinite());
} else { } else {
assert_approx_eq!(w[1], y); prop_assert!(approx_eq(w[1], y, 0.0001));
} }
if z.is_nan() || z.is_infinite() { if z.is_nan() || z.is_infinite() {
assert!(w[2].is_nan() || w[2].is_infinite()); prop_assert!(w[2].is_nan() || w[2].is_infinite());
} else { } else {
assert_approx_eq!(w[2], z); prop_assert!(approx_eq(w[2], z, 0.0001));
} }
} }
} }
@@ -281,7 +285,10 @@ mod tests {
fn test_serde() { fn test_serde() {
let a = Vector::<f32, 3>::from([1.0, 2.0, 3.0]); let a = Vector::<f32, 3>::from([1.0, 2.0, 3.0]);
let s = serde_json::to_string(&a); let s = serde_json::to_string(&a);
assert_eq!(s.as_ref().unwrap(), "{\"position\":[1.0,2.0,3.0]}"); assert_eq!(
s.as_ref().expect("serialization failed in test"),
"{\"position\":[1.0,2.0,3.0]}"
);
assert!(s.is_ok()); assert!(s.is_ok());
} }
} }
+22
View File
@@ -0,0 +1,22 @@
// This is just for basic interface and happy path testing. It doesn't do anything other than make
// sure the expected public interface exists and works.
use array_init::array_init;
use core::time::Duration;
use vivaldi_nc::NetworkCoordinate;
const NUM_NODES: usize = 100;
#[test]
fn basic_usage() {
let mut nc: [NetworkCoordinate<2>; NUM_NODES] = array_init(|_| NetworkCoordinate::<2>::new());
for i in 0..NUM_NODES {
for j in 0..NUM_NODES {
if i == j {
continue;
}
let other = nc[j].clone();
nc[i].update(&other, Duration::from_millis(100));
}
}
}