diff --git a/honeycomb-core/src/attributes/collections.rs b/honeycomb-core/src/attributes/collections.rs index 5048533a..fa66df4f 100644 --- a/honeycomb-core/src/attributes/collections.rs +++ b/honeycomb-core/src/attributes/collections.rs @@ -103,6 +103,7 @@ impl AttrSparseVec { /// - the index lands out of bounds /// - the index cannot be converted to `usize` /// + #[deprecated] pub fn get_mut(&mut self, index: T::IdentifierType) -> &mut Option { &mut self.data[index.to_usize().unwrap()] } @@ -279,6 +280,11 @@ impl AttrCompactVec { self.data.len() } + /// Return the number of stored, used attributes in the internal storage. + pub fn n_used_attributes(&self) -> usize { + self.data.len() - self.unused_data_slots.len() + } + /// Getter /// /// # Arguments @@ -317,6 +323,7 @@ impl AttrCompactVec { /// - the index lands out of bounds /// - the index cannot be converted to `usize` /// + #[deprecated] pub fn get_mut(&mut self, index: T::IdentifierType) -> Option<&mut T> { self.index_map[index.to_usize().unwrap()].map(|idx| &mut self.data[idx]) } @@ -461,42 +468,8 @@ impl AttrCompactVec { #[cfg(test)] mod tests { + use super::super::Temperature; use super::*; - use crate::{FaceIdentifier, OrbitPolicy}; - - #[derive(Clone, Copy, Debug, Default, PartialEq)] - pub struct Temperature { - pub val: f32, - } - - impl AttributeUpdate for Temperature { - fn merge(attr1: Self, attr2: Self) -> Self { - Temperature { - val: (attr1.val + attr2.val) / 2.0, - } - } - - fn split(attr: Self) -> (Self, Self) { - (attr, attr) - } - - fn merge_undefined(attr: Option) -> Self { - attr.unwrap_or(Temperature { val: 0.0 }) - } - } - - impl AttributeBind for Temperature { - type IdentifierType = FaceIdentifier; - fn binds_to<'a>() -> OrbitPolicy<'a> { - OrbitPolicy::Face - } - } - - impl From for Temperature { - fn from(val: f32) -> Self { - Self { val } - } - } macro_rules! generate_sparse { ($name: ident) => { @@ -514,6 +487,18 @@ mod tests { }; } + #[test] + fn sparse_vec_n_attributes() { + generate_sparse!(storage); + assert_eq!(storage.n_attributes(), 10); + let _ = storage.remove(3); + assert_eq!(storage.n_attributes(), 9); + // extend does not affect the number of attributes + storage.extend(10); + assert!(storage.get(15).is_none()); + assert_eq!(storage.n_attributes(), 9); + } + #[test] fn sparse_vec_get_set_get() { generate_sparse!(storage); @@ -531,8 +516,8 @@ mod tests { } #[test] - #[should_panic] - fn sparse_vec_get_insert_get() { + #[should_panic(expected = "assertion failed: tmp.is_none()")] + fn sparse_vec_insert_already_existing() { generate_sparse!(storage); assert_eq!(storage.get(3), &Some(Temperature::from(279.0))); storage.insert(3, Temperature::from(280.0)); // panic @@ -575,8 +560,8 @@ mod tests { } #[test] - #[should_panic] - fn sparse_vec_remove_replace() { + #[should_panic(expected = "called `Option::unwrap()` on a `None` value")] + fn sparse_vec_replace_already_removed() { generate_sparse!(storage); assert_eq!(storage.remove(3), Some(Temperature::from(279.0))); storage.replace(3, Temperature::from(280.0)).unwrap(); // panic @@ -598,6 +583,48 @@ mod tests { }; } + #[test] + fn compact_vec_n_attributes() { + generate_compact!(storage); + assert_eq!(storage.n_attributes(), 10); + let _ = storage.remove(3); + assert_eq!(storage.n_attributes(), 10); + // extend does not affect the number of attributes + storage.extend(10); + assert!(storage.get(15).is_none()); + assert_eq!(storage.n_attributes(), 10); + } + + #[test] + fn compact_vec_n_used_attributes() { + generate_compact!(storage); + assert_eq!(storage.n_used_attributes(), 10); + let _ = storage.remove(3); + assert_eq!(storage.n_used_attributes(), 9); + // extend does not affect the number of attributes + storage.extend(10); + assert!(storage.get(15).is_none()); + assert_eq!(storage.n_used_attributes(), 9); + } + + #[test] + fn compact_vec_extend_through_set() { + generate_compact!(storage); + assert_eq!(storage.n_attributes(), 10); + // extend does not affect the number of attributes + storage.extend(10); + assert_eq!(storage.n_attributes(), 10); + storage.set(10, Temperature::from(293.0)); + assert_eq!(storage.n_attributes(), 11); + storage.set(11, Temperature::from(295.0)); + assert_eq!(storage.n_attributes(), 12); + storage.set(12, Temperature::from(297.0)); + assert_eq!(storage.n_attributes(), 13); + let _ = storage.remove(3); + assert_eq!(storage.n_attributes(), 13); + assert_eq!(storage.n_used_attributes(), 12); + } + #[test] fn compact_vec_get_set_get() { generate_compact!(storage); @@ -615,8 +642,8 @@ mod tests { } #[test] - #[should_panic] - fn compact_vec_get_insert_get() { + #[should_panic(expected = "assertion failed: idx.is_none()")] + fn compact_vec_insert_already_existing() { generate_compact!(storage); assert_eq!(storage.get(3), Some(&Temperature::from(279.0))); storage.insert(3, Temperature::from(280.0)); // panic @@ -659,8 +686,8 @@ mod tests { } #[test] - #[should_panic] - fn compact_vec_remove_replace() { + #[should_panic(expected = "assertion failed: idx.is_some()")] + fn compact_vec_replace_already_removed() { generate_compact!(storage); assert_eq!(storage.remove(3), Some(Temperature::from(279.0))); storage.replace(3, Temperature::from(280.0)); // panic diff --git a/honeycomb-core/src/attributes/mod.rs b/honeycomb-core/src/attributes/mod.rs index 7ddcb8ed..ee190865 100644 --- a/honeycomb-core/src/attributes/mod.rs +++ b/honeycomb-core/src/attributes/mod.rs @@ -6,3 +6,43 @@ pub mod collections; pub mod traits; + +// ------ TESTS + +#[cfg(test)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +struct Temperature { + pub val: f32, +} + +#[cfg(test)] +impl crate::AttributeUpdate for Temperature { + fn merge(attr1: Self, attr2: Self) -> Self { + Temperature { + val: (attr1.val + attr2.val) / 2.0, + } + } + + fn split(attr: Self) -> (Self, Self) { + (attr, attr) + } + + fn merge_undefined(attr: Option) -> Self { + attr.unwrap_or(Temperature { val: 0.0 }) + } +} + +#[cfg(test)] +impl crate::AttributeBind for Temperature { + type IdentifierType = crate::FaceIdentifier; + fn binds_to<'a>() -> crate::OrbitPolicy<'a> { + crate::OrbitPolicy::Face + } +} + +#[cfg(test)] +impl From for Temperature { + fn from(val: f32) -> Self { + Self { val } + } +} diff --git a/honeycomb-core/src/attributes/traits.rs b/honeycomb-core/src/attributes/traits.rs index dbab7710..408785f0 100644 --- a/honeycomb-core/src/attributes/traits.rs +++ b/honeycomb-core/src/attributes/traits.rs @@ -106,3 +106,33 @@ pub trait AttributeBind: Sized { /// which the attribute is associated. fn binds_to<'a>() -> OrbitPolicy<'a>; } + +#[cfg(test)] +mod tests { + use super::super::Temperature; + use super::*; + use std::any::Any; + + #[test] + fn attribute_update() { + let t1 = Temperature { val: 273.0 }; + let t2 = Temperature { val: 298.0 }; + + let t_new = AttributeUpdate::merge(t1, t2); // use AttributeUpdate::_ + let t_ref = Temperature { val: 285.5 }; + + assert_eq!(Temperature::split(t_new), (t_ref, t_ref)); // or Temperature::_ + assert_eq!(Temperature::merge_undefined(Some(t_ref)), t_ref); + assert_eq!(Temperature::merge_undefined(None), Temperature::from(0.0)) + } + + #[test] + fn attribute_bind() { + assert_eq!(Temperature::binds_to(), crate::OrbitPolicy::Face); + let inst: ::IdentifierType = 0; + let ref_inst: crate::FaceIdentifier = 0; + let prim_inst: u32 = 0; + assert_eq!(inst.type_id(), ref_inst.type_id()); + assert_eq!(inst.type_id(), prim_inst.type_id()); + } +} diff --git a/honeycomb-core/src/cells/orbits.rs b/honeycomb-core/src/cells/orbits.rs index 0df6c43a..1a1185bb 100644 --- a/honeycomb-core/src/cells/orbits.rs +++ b/honeycomb-core/src/cells/orbits.rs @@ -15,6 +15,7 @@ use std::collections::{BTreeSet, VecDeque}; /// /// This is used to define special cases of orbits that are often used in /// algorithms. These special cases correspond to *i-cells*. +#[derive(Debug, PartialEq)] pub enum OrbitPolicy<'a> { /// 0-cell orbit. Vertex, @@ -286,14 +287,14 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "assertion failed: !slice.len().is_zero()")] fn empty_orbit_policy() { let map = simple_map(); let _ = Orbit2::new(&map, OrbitPolicy::Custom(&[]), 3); } #[test] - #[should_panic] + #[should_panic(expected = "assertion failed: i < 3")] fn invalid_orbit_policy() { let map = simple_map(); let orbit = Orbit2::new(&map, OrbitPolicy::Custom(&[6]), 3); diff --git a/honeycomb-core/src/lib.rs b/honeycomb-core/src/lib.rs index bd7c480a..8c933377 100644 --- a/honeycomb-core/src/lib.rs +++ b/honeycomb-core/src/lib.rs @@ -32,8 +32,6 @@ #![allow(clippy::semicolon_if_nothing_returned)] #![allow(clippy::needless_for_each)] #![allow(clippy::needless_pass_by_value)] -#![allow(clippy::should_panic_without_expect)] -#![allow(clippy::float_cmp)] // ------ MODULE DECLARATIONS mod attributes; diff --git a/honeycomb-core/src/spatial_repr/vector.rs b/honeycomb-core/src/spatial_repr/vector.rs index 551d8fd7..0990a7f8 100644 --- a/honeycomb-core/src/spatial_repr/vector.rs +++ b/honeycomb-core/src/spatial_repr/vector.rs @@ -292,35 +292,69 @@ mod tests { use super::*; use crate::FloatType; - fn almost_equal(lhs: &Vector2, rhs: &Vector2) -> bool { - const EPS: FloatType = 10.0e-12; - ((lhs.x() - rhs.x()).abs() < EPS) & ((lhs.y() - rhs.y()).abs() < EPS) + macro_rules! almost_equal { + ($f1: expr, $f2: expr) => { + ((($f1 - $f2) as FloatType).abs() < FloatType::EPSILON) + }; + } + + fn almost_equal_vec(lhs: &Vector2, rhs: &Vector2) -> bool { + almost_equal!(lhs.x(), rhs.x()) & almost_equal!(lhs.y(), rhs.y()) } #[test] fn dot_product() { let along_x = Vector2::unit_x() * 15.0; let along_y = Vector2::unit_y() * 10.0; - assert_eq!(along_x.dot(&along_y), 0.0); - assert_eq!(along_x.dot(&Vector2::unit_x()), 15.0); - assert_eq!(along_y.dot(&Vector2::unit_y()), 10.0); + assert!(almost_equal!(along_x.dot(&along_y), 0.0)); + assert!(almost_equal!(along_x.dot(&Vector2::unit_x()), 15.0)); + assert!(almost_equal!(along_y.dot(&Vector2::unit_y()), 10.0)); } #[test] fn unit_dir() { let along_x = Vector2::unit_x() * 4.0; let along_y = Vector2::unit_y() * 3.0; - assert_eq!(along_x.unit_dir().unwrap(), Vector2::unit_x()); - assert_eq!( - Vector2::::unit_x().unit_dir().unwrap(), - Vector2::unit_x() - ); - assert_eq!(along_y.unit_dir().unwrap(), Vector2::unit_y()); - assert!(almost_equal( + assert!(almost_equal_vec( + &along_x.unit_dir().unwrap(), + &Vector2::unit_x() + )); + assert!(almost_equal_vec( + &Vector2::::unit_x().unit_dir().unwrap(), + &Vector2::unit_x() + )); + assert!(almost_equal_vec( + &along_y.unit_dir().unwrap(), + &Vector2::unit_y() + )); + assert!(almost_equal_vec( &(along_x + along_y).unit_dir().unwrap(), &Vector2::from((4.0 / 5.0, 3.0 / 5.0)) )); let origin: Vector2 = Vector2::default(); assert!(origin.unit_dir().is_err()); } + + #[test] + fn normal_dir() { + let along_x = Vector2::unit_x() * 4.0; + let along_y = Vector2::unit_y() * 3.0; + assert!(almost_equal_vec(&along_x.normal_dir(), &Vector2::unit_y())); + assert!(almost_equal_vec( + &Vector2::unit_x().normal_dir(), + &Vector2::unit_y() + )); + assert!(almost_equal_vec(&along_y.normal_dir(), &-Vector2::unit_x())); + assert!(almost_equal_vec( + &Vector2::unit_y().normal_dir(), + &-Vector2::unit_x() + )); + } + + #[test] + #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidUnitDir")] + fn normal_dir_of_null_vel() { + let origin: Vector2 = Vector2::default(); + let _ = origin.normal_dir(); // panics + } } diff --git a/honeycomb-core/src/twomap.rs b/honeycomb-core/src/twomap.rs index 45687998..a94476ce 100644 --- a/honeycomb-core/src/twomap.rs +++ b/honeycomb-core/src/twomap.rs @@ -1469,7 +1469,7 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: UndefinedVertex")] fn remove_vertex_twice() { // in its default state, all darts/vertices of a map are considered to be used let mut map: CMap2 = CMap2::new(4); @@ -1480,7 +1480,7 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "assertion failed: self.unused_darts.insert(dart_id)")] fn remove_dart_twice() { // in its default state, all darts/vertices of a map are considered to be used // darts are also free @@ -1490,4 +1490,121 @@ mod tests { // set dart 1 as unused, again map.remove_free_dart(1); // this should panic } + + #[test] + fn two_sew_complete() { + let mut map: CMap2 = CMap2::new(4); + map.one_link(1, 2); + map.one_link(3, 4); + map.insert_vertex(1, (0.0, 0.0)); + map.insert_vertex(2, (0.0, 1.0)); + map.insert_vertex(3, (1.0, 1.0)); + map.insert_vertex(4, (1.0, 0.0)); + map.two_sew(1, 3); + assert_eq!(map.vertex(1), Vertex2::from((0.5, 0.0))); + assert_eq!(map.vertex(2), Vertex2::from((0.5, 1.0))); + } + + #[test] + fn two_sew_incomplete() { + let mut map: CMap2 = CMap2::new(3); + map.one_link(1, 2); + map.insert_vertex(1, (0.0, 0.0)); + map.insert_vertex(2, (0.0, 1.0)); + map.insert_vertex(3, (1.0, 1.0)); + map.two_sew(1, 3); + // missing beta1 image for dart 3 + assert_eq!(map.vertex(1), Vertex2::from((0.0, 0.0))); + assert_eq!(map.vertex(2), Vertex2::from((0.5, 1.0))); + map.two_unsew(1); + assert_eq!(map.add_free_dart(), 4); + map.one_link(3, 4); + map.two_sew(1, 3); + // missing vertex for dart 4 + assert_eq!(map.vertex(1), Vertex2::from((0.0, 0.0))); + assert_eq!(map.vertex(2), Vertex2::from((0.5, 1.0))); + } + + #[test] + fn two_sew_no_b1() { + let mut map: CMap2 = CMap2::new(2); + map.insert_vertex(1, (0.0, 0.0)); + map.insert_vertex(2, (1.0, 1.0)); + map.two_sew(1, 2); + assert_eq!(map.vertex(1), Vertex2::from((0.0, 0.0))); + assert_eq!(map.vertex(2), Vertex2::from((1.0, 1.0))); + } + + #[test] + //#[should_panic] + fn two_sew_no_attributes() { + let mut map: CMap2 = CMap2::new(2); + map.two_sew(1, 2); // should panic + } + + #[test] + #[should_panic(expected = "called `Option::unwrap()` on a `None` value")] + fn two_sew_no_attributes_bis() { + let mut map: CMap2 = CMap2::new(4); + map.one_link(1, 2); + map.one_link(3, 4); + map.two_sew(1, 3); // panic + } + + #[test] + #[should_panic(expected = "Dart 1 and 3 do not have consistent orientation for 2-sewing")] + fn two_sew_bad_orientation() { + let mut map: CMap2 = CMap2::new(4); + map.one_link(1, 2); + map.one_link(3, 4); + map.insert_vertex(1, (0.0, 0.0)); + map.insert_vertex(2, (0.0, 1.0)); // 1->2 goes up + map.insert_vertex(3, (1.0, 0.0)); + map.insert_vertex(4, (1.0, 1.0)); // 3->4 also goes up + map.two_sew(1, 3); // panic + } + + #[test] + fn one_sew_complete() { + let mut map: CMap2 = CMap2::new(3); + map.two_link(1, 2); + map.insert_vertex(1, (0.0, 0.0)); + map.insert_vertex(2, (0.0, 1.0)); + map.insert_vertex(3, (0.0, 2.0)); + map.one_sew(1, 3); + assert_eq!(map.vertex(2), Vertex2::from((0.0, 1.5))); + } + + #[test] + fn one_sew_incomplete_attributes() { + let mut map: CMap2 = CMap2::new(3); + map.two_link(1, 2); + map.insert_vertex(1, (0.0, 0.0)); + map.insert_vertex(2, (0.0, 1.0)); + map.one_sew(1, 3); + assert_eq!(map.vertex(2), Vertex2::from((0.0, 1.0))); + } + + #[test] + fn one_sew_incomplete_beta() { + let mut map: CMap2 = CMap2::new(3); + map.insert_vertex(1, (0.0, 0.0)); + map.insert_vertex(2, (0.0, 1.0)); + map.one_sew(1, 2); + assert_eq!(map.vertex(2), Vertex2::from((0.0, 1.0))); + } + #[test] + //#[should_panic] + fn one_sew_no_attributes() { + let mut map: CMap2 = CMap2::new(2); + map.one_sew(1, 2); // should panic + } + + #[test] + #[should_panic(expected = "called `Option::unwrap()` on a `None` value")] + fn one_sew_no_attributes_bis() { + let mut map: CMap2 = CMap2::new(3); + map.two_link(1, 2); + map.one_sew(1, 3); // panic + } }