From c484c9fce9e546122cae3b8810323ac4f60a5768 Mon Sep 17 00:00:00 2001 From: Rui Hu Date: Sat, 8 Nov 2025 17:59:16 -0800 Subject: [PATCH] refactor: split integration tests by feature --- tests/_util.rs | 37 ++ .../{count_dist.rs => distance_call_count.rs} | 0 tests/feature_nearest.rs | 298 +++++++++++++ tests/feature_within.rs | 66 +++ tests/kdtree.rs | 421 ------------------ tests/{serialization.rs => serde.rs} | 0 6 files changed, 401 insertions(+), 421 deletions(-) create mode 100644 tests/_util.rs rename tests/{count_dist.rs => distance_call_count.rs} (100%) create mode 100644 tests/feature_nearest.rs create mode 100644 tests/feature_within.rs delete mode 100644 tests/kdtree.rs rename tests/{serialization.rs => serde.rs} (100%) diff --git a/tests/_util.rs b/tests/_util.rs new file mode 100644 index 0000000..7b3a2bb --- /dev/null +++ b/tests/_util.rs @@ -0,0 +1,37 @@ +use kdtree::KdTree; + +pub mod fixtures { + use super::KdTree; + + pub const POINT_A: ([f64; 2], usize) = ([0f64, 0f64], 0); + pub const POINT_B: ([f64; 2], usize) = ([1f64, 1f64], 1); + pub const POINT_C: ([f64; 2], usize) = ([2f64, 2f64], 2); + pub const POINT_D: ([f64; 2], usize) = ([3f64, 3f64], 3); + + pub fn basic_tree() -> KdTree { + let mut tree = KdTree::with_capacity(2, 2); + for &(coords, value) in &[POINT_A, POINT_B, POINT_C, POINT_D] { + tree.add(coords, value).unwrap(); + } + tree + } +} + +pub mod assertions { + pub fn assert_ordered_usize(results: Vec<(f64, &usize)>, expected: &[(f64, usize)]) { + assert_eq!(normalize(results), expected); + } + + #[allow(dead_code)] // some test crates (e.g., nearest) do not use this helper yet + pub fn assert_unordered_usize(results: Vec<(f64, &usize)>, expected: &[(f64, usize)]) { + let mut actual = normalize(results); + let mut expected_vec = expected.to_vec(); + actual.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap().then_with(|| a.1.cmp(&b.1))); + expected_vec.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap().then_with(|| a.1.cmp(&b.1))); + assert_eq!(actual, expected_vec); + } + + fn normalize(results: Vec<(f64, &usize)>) -> Vec<(f64, usize)> { + results.into_iter().map(|(d, value)| (d, *value)).collect() + } +} diff --git a/tests/count_dist.rs b/tests/distance_call_count.rs similarity index 100% rename from tests/count_dist.rs rename to tests/distance_call_count.rs diff --git a/tests/feature_nearest.rs b/tests/feature_nearest.rs new file mode 100644 index 0000000..db2ff5d --- /dev/null +++ b/tests/feature_nearest.rs @@ -0,0 +1,298 @@ +mod _util; + +use _util::assertions::assert_ordered_usize; +use _util::fixtures::{POINT_A, POINT_B, POINT_C, basic_tree}; +use kdtree::distance::squared_euclidean; +use kdtree::{ErrorKind, KdTree}; + +#[test] +fn nearest_queries_match_expected() { + let tree = basic_tree(); + assert_eq!(tree.size(), 4); + + assert_ordered_usize(tree.nearest(&POINT_A.0, 0, &squared_euclidean).unwrap(), &[]); + assert_ordered_usize(tree.nearest(&POINT_A.0, 1, &squared_euclidean).unwrap(), &[(0f64, 0)]); + assert_ordered_usize( + tree.nearest(&POINT_A.0, 2, &squared_euclidean).unwrap(), + &[(0f64, 0), (2f64, 1)], + ); + assert_ordered_usize( + tree.nearest(&POINT_A.0, 3, &squared_euclidean).unwrap(), + &[(0f64, 0), (2f64, 1), (8f64, 2)], + ); + assert_ordered_usize( + tree.nearest(&POINT_A.0, 4, &squared_euclidean).unwrap(), + &[(0f64, 0), (2f64, 1), (8f64, 2), (18f64, 3)], + ); + assert_ordered_usize( + tree.nearest(&POINT_A.0, 5, &squared_euclidean).unwrap(), + &[(0f64, 0), (2f64, 1), (8f64, 2), (18f64, 3)], + ); + assert_ordered_usize( + tree.nearest(&POINT_B.0, 4, &squared_euclidean).unwrap(), + &[(0f64, 1), (2f64, 0), (2f64, 2), (8f64, 3)], + ); +} + +#[test] +fn iter_nearest_matches_batch() { + let tree = basic_tree(); + let collected: Vec<_> = tree.iter_nearest(&POINT_A.0, &squared_euclidean).unwrap().collect(); + assert_ordered_usize(collected, &[(0f64, 0), (2f64, 1), (8f64, 2), (18f64, 3)]); +} + +#[test] +fn iter_nearest_mut_applies_updates() { + let mut tree = basic_tree(); + let (dist, value) = tree + .iter_nearest_mut(&POINT_A.0, &squared_euclidean) + .unwrap() + .next() + .unwrap(); + assert_eq!(dist, 0f64); + *value = 10; + + let collected: Vec<_> = tree.iter_nearest(&POINT_A.0, &squared_euclidean).unwrap().collect(); + assert_ordered_usize(collected, &[(0f64, 10), (2f64, 1), (8f64, 2), (18f64, 3)]); +} + +#[test] +fn nearest_supports_vec_points() { + let dimensions = 2; + let capacity_per_node = 2; + let mut tree = KdTree::with_capacity(dimensions, capacity_per_node); + + tree.add(vec![0.0; 2], 0).unwrap(); + tree.add(vec![1.0; 2], 1).unwrap(); + tree.add(vec![2.0; 2], 2).unwrap(); + tree.add(vec![3.0; 2], 3).unwrap(); + + assert_ordered_usize(tree.nearest(&POINT_A.0, 0, &squared_euclidean).unwrap(), &[]); + assert_ordered_usize(tree.nearest(&POINT_A.0, 1, &squared_euclidean).unwrap(), &[(0f64, 0)]); + assert_ordered_usize( + tree.nearest(&POINT_A.0, 2, &squared_euclidean).unwrap(), + &[(0f64, 0), (2f64, 1)], + ); + assert_ordered_usize( + tree.nearest(&POINT_A.0, 3, &squared_euclidean).unwrap(), + &[(0f64, 0), (2f64, 1), (8f64, 2)], + ); + assert_ordered_usize( + tree.nearest(&POINT_A.0, 4, &squared_euclidean).unwrap(), + &[(0f64, 0), (2f64, 1), (8f64, 2), (18f64, 3)], + ); + assert_ordered_usize( + tree.nearest(&POINT_A.0, 5, &squared_euclidean).unwrap(), + &[(0f64, 0), (2f64, 1), (8f64, 2), (18f64, 3)], + ); + assert_ordered_usize( + tree.nearest(&POINT_B.0, 4, &squared_euclidean).unwrap(), + &[(0f64, 1), (2f64, 0), (2f64, 2), (8f64, 3)], + ); +} + +#[test] +fn handles_zero_capacity() { + let mut tree = KdTree::with_capacity(2, 0); + assert_eq!(tree.add(&POINT_A.0, POINT_A.1), Err(ErrorKind::ZeroCapacity)); + assert_ordered_usize(tree.nearest(&POINT_A.0, 1, &squared_euclidean).unwrap(), &[]); +} + +#[test] +fn handles_wrong_dimension() { + let point = ([0f64], 0f64); + let mut tree = KdTree::with_capacity(2, 1); + + assert_eq!(tree.add(&point.0, point.1), Err(ErrorKind::WrongDimension)); + assert_eq!( + tree.nearest(&point.0, 1, &squared_euclidean), + Err(ErrorKind::WrongDimension) + ); +} + +#[test] +fn handles_non_finite_coordinate() { + let point_a = ([f64::NAN, f64::NAN], 0f64); + let point_b = ([f64::INFINITY, f64::INFINITY], 0f64); + let mut tree = KdTree::with_capacity(2, 1); + + assert_eq!(tree.add(&point_a.0, point_a.1), Err(ErrorKind::NonFiniteCoordinate)); + assert_eq!(tree.add(&point_b.0, point_b.1), Err(ErrorKind::NonFiniteCoordinate)); + assert_eq!( + tree.nearest(&point_a.0, 1, &squared_euclidean), + Err(ErrorKind::NonFiniteCoordinate) + ); + assert_eq!( + tree.nearest(&point_b.0, 1, &squared_euclidean), + Err(ErrorKind::NonFiniteCoordinate) + ); +} + +#[test] +fn handles_singularity() { + let mut tree = KdTree::with_capacity(2, 1); + tree.add(&POINT_A.0, POINT_A.1).unwrap(); + tree.add(&POINT_A.0, POINT_A.1).unwrap(); + tree.add(&POINT_A.0, POINT_A.1).unwrap(); + tree.add(&POINT_B.0, POINT_B.1).unwrap(); + tree.add(&POINT_B.0, POINT_B.1).unwrap(); + tree.add(&POINT_B.0, POINT_B.1).unwrap(); + tree.add(&POINT_C.0, POINT_C.1).unwrap(); + tree.add(&POINT_C.0, POINT_C.1).unwrap(); + tree.add(&POINT_C.0, POINT_C.1).unwrap(); + assert_eq!(tree.size(), 9); +} + +#[test] +fn handles_pending_order() { + let item1 = ([0f64], 1); + let item2 = ([100f64], 2); + let item3 = ([45f64], 3); + let item4 = ([55f64], 4); + + let mut tree = KdTree::with_capacity(1, 2); + tree.add(&item1.0, item1.1).unwrap(); + tree.add(&item2.0, item2.1).unwrap(); + tree.add(&item3.0, item3.1).unwrap(); + tree.add(&item4.0, item4.1).unwrap(); + + assert_ordered_usize( + tree.nearest(&[51f64], 2, &squared_euclidean).unwrap(), + &[(16.0, 4), (36.0, 3)], + ); + assert_ordered_usize( + tree.nearest(&[51f64], 4, &squared_euclidean).unwrap(), + &[(16.0, 4), (36.0, 3), (2401.0, 2), (2601.0, 1)], + ); + assert_ordered_usize( + tree.nearest(&[49f64], 2, &squared_euclidean).unwrap(), + &[(16.0, 3), (36.0, 4)], + ); + assert_ordered_usize( + tree.nearest(&[49f64], 4, &squared_euclidean).unwrap(), + &[(16.0, 3), (36.0, 4), (2401.0, 1), (2601.0, 2)], + ); + + let collected: Vec<_> = tree.iter_nearest(&[49f64], &squared_euclidean).unwrap().collect(); + assert_ordered_usize(collected, &[(16.0, 3), (36.0, 4), (2401.0, 1), (2601.0, 2)]); + let collected: Vec<_> = tree.iter_nearest(&[51f64], &squared_euclidean).unwrap().collect(); + assert_ordered_usize(collected, &[(16.0, 4), (36.0, 3), (2401.0, 2), (2601.0, 1)]); +} + +#[test] +fn handles_drops_correctly() { + use std::ops::Drop; + use std::sync::{Arc, Mutex}; + + struct Test(Arc>); + + impl PartialEq for Test { + fn eq(&self, other: &Test) -> bool { + *self.0.lock().unwrap() == *other.0.lock().unwrap() + } + } + + impl Drop for Test { + fn drop(&mut self) { + let mut drop_counter = self.0.lock().unwrap(); + *drop_counter += 1; + } + } + + let drop_counter = Arc::new(Mutex::new(0)); + + let item1 = ([0f64, 0f64], Test(drop_counter.clone())); + let item2 = ([1f64, 1f64], Test(drop_counter.clone())); + let item3 = ([2f64, 2f64], Test(drop_counter.clone())); + let item4 = ([3f64, 3f64], Test(drop_counter.clone())); + + { + let mut tree = KdTree::with_capacity(2, 1); + tree.add(&item1.0, item1.1).unwrap(); + tree.add(&item2.0, item2.1).unwrap(); + tree.add(&item3.0, item3.1).unwrap(); + tree.add(&item4.0, item4.1).unwrap(); + assert_eq!(*drop_counter.lock().unwrap(), 0); + } + + assert_eq!(*drop_counter.lock().unwrap(), 4); +} + +#[test] +fn handles_remove_correctly() { + let item1 = ([0f64], 1); + let item2 = ([100f64], 2); + let item3 = ([45f64], 3); + let item4 = ([55f64], 4); + + let mut tree = KdTree::with_capacity(1, 2); + tree.add(&item1.0, item1.1).unwrap(); + tree.add(&item2.0, item2.1).unwrap(); + tree.add(&item3.0, item3.1).unwrap(); + tree.add(&item4.0, item4.1).unwrap(); + + let removed = tree.remove(&&item3.0, &item3.1).unwrap(); + assert_eq!(tree.size(), 3); + assert_eq!(removed, 1); + assert_ordered_usize( + tree.nearest(&[51f64], 2, &squared_euclidean).unwrap(), + &[(16.0, 4), (2401.0, 2)], + ); +} + +#[test] +fn handles_remove_multiple_match() { + let item1 = ([0f64], 1); + let item2 = ([0f64], 1); + let item3 = ([100f64], 2); + let item4 = ([45f64], 3); + + let mut tree = KdTree::with_capacity(1, 2); + tree.add(&item1.0, item1.1).unwrap(); + tree.add(&item2.0, item2.1).unwrap(); + tree.add(&item3.0, item3.1).unwrap(); + tree.add(&item4.0, item4.1).unwrap(); + + assert_eq!(tree.size(), 4); + let removed = tree.remove(&&[0f64], &1).unwrap(); + assert_eq!(tree.size(), 2); + assert_eq!(removed, 2); + assert_ordered_usize(tree.nearest(&[45f64], 1, &squared_euclidean).unwrap(), &[(0.0, 3)]); +} + +#[test] +fn handles_remove_no_match() { + let item1 = ([0f64], 1); + let item2 = ([100f64], 2); + let item3 = ([45f64], 3); + let item4 = ([55f64], 4); + + let mut tree = KdTree::with_capacity(1, 2); + tree.add(&item1.0, item1.1).unwrap(); + tree.add(&item2.0, item2.1).unwrap(); + tree.add(&item3.0, item3.1).unwrap(); + tree.add(&item4.0, item4.1).unwrap(); + + let removed = tree.remove(&&[1f64], &2).unwrap(); + assert_eq!(tree.size(), 4); + assert_eq!(removed, 0); + assert_ordered_usize( + tree.nearest(&[51f64], 2, &squared_euclidean).unwrap(), + &[(16.0, 4), (36.0, 3)], + ); +} + +#[test] +fn handles_remove_overlapping_points() { + let a = ([0f64, 0f64], 0); + let b = ([0f64, 0f64], 1); + let mut tree = KdTree::new(2); + + tree.add(a.0, a.1).unwrap(); + tree.add(b.0, b.1).unwrap(); + + let removed = tree.remove(&[0f64, 0f64], &1).unwrap(); + assert_eq!(tree.size(), 1); + assert_eq!(removed, 1); + assert_ordered_usize(tree.nearest(&[0f64, 0f64], 1, &squared_euclidean).unwrap(), &[(0.0, 0)]); +} diff --git a/tests/feature_within.rs b/tests/feature_within.rs new file mode 100644 index 0000000..fcb8496 --- /dev/null +++ b/tests/feature_within.rs @@ -0,0 +1,66 @@ +mod _util; + +use _util::assertions::{assert_ordered_usize, assert_unordered_usize}; +use _util::fixtures::{POINT_A, POINT_B, basic_tree}; +use kdtree::KdTree; +use kdtree::distance::squared_euclidean; + +#[test] +fn within_queries_match_expected() { + let tree = basic_tree(); + + assert_ordered_usize(tree.within(&POINT_A.0, 0.0, &squared_euclidean).unwrap(), &[(0.0, 0)]); + assert_ordered_usize(tree.within(&POINT_B.0, 1.0, &squared_euclidean).unwrap(), &[(0.0, 1)]); + assert_ordered_usize( + tree.within(&POINT_B.0, 2.0, &squared_euclidean).unwrap(), + &[(0.0, 1), (2.0, 2), (2.0, 0)], + ); +} + +#[test] +fn within_unsorted_matches_expected() { + let tree = basic_tree(); + + assert_unordered_usize( + tree.within_unsorted(&POINT_A.0, 0.0, &squared_euclidean).unwrap(), + &[(0.0, 0)], + ); + assert_eq!(tree.within_count(&POINT_A.0, 0.0, &squared_euclidean).unwrap(), 1); + + assert_unordered_usize( + tree.within_unsorted(&POINT_B.0, 1.0, &squared_euclidean).unwrap(), + &[(0.0, 1)], + ); + assert_eq!(tree.within_count(&POINT_B.0, 1.0, &squared_euclidean).unwrap(), 1); + + assert_unordered_usize( + tree.within_unsorted(&POINT_B.0, 2.0, &squared_euclidean).unwrap(), + &[(0.0, 1), (2.0, 2), (2.0, 0)], + ); + assert_eq!(tree.within_count(&POINT_B.0, 2.0, &squared_euclidean).unwrap(), 3); +} + +#[test] +fn within_handles_pending_order_tree() { + let item1 = ([0f64], 1); + let item2 = ([100f64], 2); + let item3 = ([45f64], 3); + let item4 = ([55f64], 4); + + let mut tree = KdTree::with_capacity(1, 2); + tree.add(&item1.0, item1.1).unwrap(); + tree.add(&item2.0, item2.1).unwrap(); + tree.add(&item3.0, item3.1).unwrap(); + tree.add(&item4.0, item4.1).unwrap(); + + assert_ordered_usize( + tree.within(&[50f64], 25.0, &squared_euclidean).unwrap(), + &[(25.0, 3), (25.0, 4)], + ); + assert_ordered_usize( + tree.within(&[50f64], 30.0, &squared_euclidean).unwrap(), + &[(25.0, 3), (25.0, 4)], + ); + assert_ordered_usize(tree.within(&[55f64], 5.0, &squared_euclidean).unwrap(), &[(0.0, 4)]); + assert_ordered_usize(tree.within(&[56f64], 5.0, &squared_euclidean).unwrap(), &[(1.0, 4)]); +} diff --git a/tests/kdtree.rs b/tests/kdtree.rs deleted file mode 100644 index 495c81c..0000000 --- a/tests/kdtree.rs +++ /dev/null @@ -1,421 +0,0 @@ -extern crate kdtree; - -use kdtree::ErrorKind; -use kdtree::KdTree; -use kdtree::distance::squared_euclidean; - -static POINT_A: ([f64; 2], usize) = ([0f64, 0f64], 0); -static POINT_B: ([f64; 2], usize) = ([1f64, 1f64], 1); -static POINT_C: ([f64; 2], usize) = ([2f64, 2f64], 2); -static POINT_D: ([f64; 2], usize) = ([3f64, 3f64], 3); - -#[test] -fn it_works() { - let dimensions = 2; - let capacity_per_node = 2; - let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node); - - kdtree.add(&POINT_A.0, POINT_A.1).unwrap(); - kdtree.add(&POINT_B.0, POINT_B.1).unwrap(); - kdtree.add(&POINT_C.0, POINT_C.1).unwrap(); - kdtree.add(&POINT_D.0, POINT_D.1).unwrap(); - - assert_eq!(kdtree.size(), 4); - assert_eq!(kdtree.nearest(&POINT_A.0, 0, &squared_euclidean).unwrap(), vec![]); - assert_eq!( - kdtree.nearest(&POINT_A.0, 1, &squared_euclidean).unwrap(), - vec![(0f64, &0)] - ); - assert_eq!( - kdtree.nearest(&POINT_A.0, 2, &squared_euclidean).unwrap(), - vec![(0f64, &0), (2f64, &1)] - ); - assert_eq!( - kdtree.nearest(&POINT_A.0, 3, &squared_euclidean).unwrap(), - vec![(0f64, &0), (2f64, &1), (8f64, &2)] - ); - assert_eq!( - kdtree.nearest(&POINT_A.0, 4, &squared_euclidean).unwrap(), - vec![(0f64, &0), (2f64, &1), (8f64, &2), (18f64, &3)] - ); - assert_eq!( - kdtree.nearest(&POINT_A.0, 5, &squared_euclidean).unwrap(), - vec![(0f64, &0), (2f64, &1), (8f64, &2), (18f64, &3)] - ); - assert_eq!( - kdtree.nearest(&POINT_B.0, 4, &squared_euclidean).unwrap(), - vec![(0f64, &1), (2f64, &0), (2f64, &2), (8f64, &3)] - ); - - assert_eq!( - kdtree.within(&POINT_A.0, 0.0, &squared_euclidean).unwrap(), - vec![(0.0, &0)] - ); - assert_eq!( - kdtree.within(&POINT_B.0, 1.0, &squared_euclidean).unwrap(), - vec![(0.0, &1)] - ); - assert_eq!( - kdtree.within(&POINT_B.0, 2.0, &squared_euclidean).unwrap(), - vec![(0.0, &1), (2.0, &2), (2.0, &0)] - ); - - let unsorted1 = kdtree.within_unsorted(&POINT_A.0, 0.0, &squared_euclidean).unwrap(); - let ans1 = [(0.0, &0)]; - assert_eq!(unsorted1.len(), ans1.len()); - assert_eq!( - kdtree.within_count(&POINT_A.0, 0.0, &squared_euclidean).unwrap(), - ans1.len() - ); - for item in unsorted1 { - assert!(ans1.contains(&item)); - } - - let unsorted2 = kdtree.within_unsorted(&POINT_B.0, 1.0, &squared_euclidean).unwrap(); - let ans2 = [(0.0, &1)]; - assert_eq!(unsorted2.len(), ans2.len()); - assert_eq!( - kdtree.within_count(&POINT_B.0, 1.0, &squared_euclidean).unwrap(), - ans2.len() - ); - for item in unsorted2 { - assert!(ans2.contains(&item)); - } - - let unsorted3 = kdtree.within_unsorted(&POINT_B.0, 2.0, &squared_euclidean).unwrap(); - let ans3 = [(0.0, &1), (2.0, &2), (2.0, &0)]; - assert_eq!(unsorted3.len(), ans3.len()); - assert_eq!( - kdtree.within_count(&POINT_B.0, 2.0, &squared_euclidean).unwrap(), - ans3.len() - ); - for item in unsorted3 { - assert!(ans3.contains(&item)); - } - - assert_eq!( - kdtree - .iter_nearest(&POINT_A.0, &squared_euclidean) - .unwrap() - .collect::>(), - vec![(0f64, &0), (2f64, &1), (8f64, &2), (18f64, &3)] - ); - - let iter = kdtree - .iter_nearest_mut(&POINT_A.0, &squared_euclidean) - .unwrap() - .next() - .unwrap(); - *iter.1 = 10; - - assert_eq!( - kdtree - .iter_nearest(&POINT_A.0, &squared_euclidean) - .unwrap() - .collect::>(), - vec![(0f64, &10), (2f64, &1), (8f64, &2), (18f64, &3)] - ); -} - -#[test] -fn it_works_with_vec() { - let dimensions = 2; - let capacity_per_node = 2; - let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node); - - kdtree.add(vec![0.0; 2], 0).unwrap(); - kdtree.add(vec![1.0; 2], 1).unwrap(); - kdtree.add(vec![2.0; 2], 2).unwrap(); - kdtree.add(vec![3.0; 2], 3).unwrap(); - - assert_eq!(kdtree.size(), 4); - assert_eq!(kdtree.nearest(&POINT_A.0, 0, &squared_euclidean).unwrap(), vec![]); - assert_eq!( - kdtree.nearest(&POINT_A.0, 1, &squared_euclidean).unwrap(), - vec![(0f64, &0)] - ); - assert_eq!( - kdtree.nearest(&POINT_A.0, 2, &squared_euclidean).unwrap(), - vec![(0f64, &0), (2f64, &1)] - ); - assert_eq!( - kdtree.nearest(&POINT_A.0, 3, &squared_euclidean).unwrap(), - vec![(0f64, &0), (2f64, &1), (8f64, &2)] - ); - assert_eq!( - kdtree.nearest(&POINT_A.0, 4, &squared_euclidean).unwrap(), - vec![(0f64, &0), (2f64, &1), (8f64, &2), (18f64, &3)] - ); - assert_eq!( - kdtree.nearest(&POINT_A.0, 5, &squared_euclidean).unwrap(), - vec![(0f64, &0), (2f64, &1), (8f64, &2), (18f64, &3)] - ); - assert_eq!( - kdtree.nearest(&POINT_B.0, 4, &squared_euclidean).unwrap(), - vec![(0f64, &1), (2f64, &0), (2f64, &2), (8f64, &3)] - ); -} - -#[test] -fn handles_zero_capacity() { - let mut kdtree = KdTree::with_capacity(2, 0); - - assert_eq!(kdtree.add(&POINT_A.0, POINT_A.1), Err(ErrorKind::ZeroCapacity)); - assert_eq!(kdtree.nearest(&POINT_A.0, 1, &squared_euclidean).unwrap(), vec![]); -} - -#[test] -fn handles_wrong_dimension() { - let point = ([0f64], 0f64); - let mut kdtree = KdTree::with_capacity(2, 1); - - assert_eq!(kdtree.add(&point.0, point.1), Err(ErrorKind::WrongDimension)); - assert_eq!( - kdtree.nearest(&point.0, 1, &squared_euclidean), - Err(ErrorKind::WrongDimension) - ); -} - -#[test] -fn handles_non_finite_coordinate() { - let point_a = ([f64::NAN, f64::NAN], 0f64); - let point_b = ([f64::INFINITY, f64::INFINITY], 0f64); - let mut kdtree = KdTree::with_capacity(2, 1); - - assert_eq!(kdtree.add(&point_a.0, point_a.1), Err(ErrorKind::NonFiniteCoordinate)); - assert_eq!(kdtree.add(&point_b.0, point_b.1), Err(ErrorKind::NonFiniteCoordinate)); - assert_eq!( - kdtree.nearest(&point_a.0, 1, &squared_euclidean), - Err(ErrorKind::NonFiniteCoordinate) - ); - assert_eq!( - kdtree.nearest(&point_b.0, 1, &squared_euclidean), - Err(ErrorKind::NonFiniteCoordinate) - ); -} - -#[test] -fn handles_singularity() { - let mut kdtree = KdTree::with_capacity(2, 1); - kdtree.add(&POINT_A.0, POINT_A.1).unwrap(); - kdtree.add(&POINT_A.0, POINT_A.1).unwrap(); - kdtree.add(&POINT_A.0, POINT_A.1).unwrap(); - kdtree.add(&POINT_B.0, POINT_B.1).unwrap(); - kdtree.add(&POINT_B.0, POINT_B.1).unwrap(); - kdtree.add(&POINT_B.0, POINT_B.1).unwrap(); - kdtree.add(&POINT_C.0, POINT_C.1).unwrap(); - kdtree.add(&POINT_C.0, POINT_C.1).unwrap(); - kdtree.add(&POINT_C.0, POINT_C.1).unwrap(); - assert_eq!(kdtree.size(), 9); -} - -#[test] -fn handles_pending_order() { - let item1 = ([0f64], 1); - let item2 = ([100f64], 2); - let item3 = ([45f64], 3); - let item4 = ([55f64], 4); - - // Build a kd tree - let dimensions = 1; - let capacity_per_node = 2; - let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node); - - kdtree.add(&item1.0, item1.1).unwrap(); - kdtree.add(&item2.0, item2.1).unwrap(); - kdtree.add(&item3.0, item3.1).unwrap(); - kdtree.add(&item4.0, item4.1).unwrap(); - assert_eq!( - kdtree.nearest(&[51f64], 2, &squared_euclidean).unwrap(), - vec![(16.0, &4), (36.0, &3)] - ); - assert_eq!( - kdtree.nearest(&[51f64], 4, &squared_euclidean).unwrap(), - vec![(16.0, &4), (36.0, &3), (2401.0, &2), (2601.0, &1)] - ); - assert_eq!( - kdtree.nearest(&[49f64], 2, &squared_euclidean).unwrap(), - vec![(16.0, &3), (36.0, &4)] - ); - assert_eq!( - kdtree.nearest(&[49f64], 4, &squared_euclidean).unwrap(), - vec![(16.0, &3), (36.0, &4), (2401.0, &1), (2601.0, &2)] - ); - - assert_eq!( - kdtree - .iter_nearest(&[49f64], &squared_euclidean) - .unwrap() - .collect::>(), - vec![(16.0, &3), (36.0, &4), (2401.0, &1), (2601.0, &2)] - ); - assert_eq!( - kdtree - .iter_nearest(&[51f64], &squared_euclidean) - .unwrap() - .collect::>(), - vec![(16.0, &4), (36.0, &3), (2401.0, &2), (2601.0, &1)] - ); - - assert_eq!(kdtree.within(&[50f64], 1.0, &squared_euclidean).unwrap(), vec![]); - assert_eq!( - kdtree.within(&[50f64], 25.0, &squared_euclidean).unwrap(), - vec![(25.0, &3), (25.0, &4)] - ); - assert_eq!( - kdtree.within(&[50f64], 30.0, &squared_euclidean).unwrap(), - vec![(25.0, &3), (25.0, &4)] - ); - assert_eq!( - kdtree.within(&[55f64], 5.0, &squared_euclidean).unwrap(), - vec![(0.0, &4)] - ); - assert_eq!( - kdtree.within(&[56f64], 5.0, &squared_euclidean).unwrap(), - vec![(1.0, &4)] - ); -} - -#[test] -fn handles_drops_correctly() { - use std::ops::Drop; - use std::sync::{Arc, Mutex}; - - // Mock up a structure to keep track of Drops - struct Test(Arc>); - - impl PartialEq for Test { - fn eq(&self, other: &Test) -> bool { - *self.0.lock().unwrap() == *other.0.lock().unwrap() - } - } - - impl Drop for Test { - fn drop(&mut self) { - let mut drop_counter = self.0.lock().unwrap(); - *drop_counter += 1; - } - } - - let drop_counter = Arc::new(Mutex::new(0)); - - let item1 = ([0f64, 0f64], Test(drop_counter.clone())); - let item2 = ([1f64, 1f64], Test(drop_counter.clone())); - let item3 = ([2f64, 2f64], Test(drop_counter.clone())); - let item4 = ([3f64, 3f64], Test(drop_counter.clone())); - - { - // Build a kd tree - let dimensions = 2; - let capacity_per_node = 1; - let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node); - - kdtree.add(&item1.0, item1.1).unwrap(); - kdtree.add(&item2.0, item2.1).unwrap(); - kdtree.add(&item3.0, item3.1).unwrap(); - kdtree.add(&item4.0, item4.1).unwrap(); - - // Pre-drop check - assert_eq!(*drop_counter.lock().unwrap(), 0); - } - - // Post-drop check - assert_eq!(*drop_counter.lock().unwrap(), 4); -} - -#[test] -fn handles_remove_correctly() { - let item1 = ([0f64], 1); - let item2 = ([100f64], 2); - let item3 = ([45f64], 3); - let item4 = ([55f64], 4); - - // Build a kd tree - let dimensions = 1; - let capacity_per_node = 2; - let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node); - - kdtree.add(&item1.0, item1.1).unwrap(); - kdtree.add(&item2.0, item2.1).unwrap(); - kdtree.add(&item3.0, item3.1).unwrap(); - kdtree.add(&item4.0, item4.1).unwrap(); - - let num_removed = kdtree.remove(&&item3.0, &item3.1).unwrap(); - assert_eq!(kdtree.size(), 3); - assert_eq!(num_removed, 1); - assert_eq!( - kdtree.nearest(&[51f64], 2, &squared_euclidean).unwrap(), - vec![(16.0, &4), (2401.0, &2)] - ); -} - -#[test] -fn handles_remove_multiple_match() { - let item1 = ([0f64], 1); - let item2 = ([0f64], 1); - let item3 = ([100f64], 2); - let item4 = ([45f64], 3); - - // Build a kd tree - let dimensions = 1; - let capacity_per_node = 2; - let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node); - - kdtree.add(&item1.0, item1.1).unwrap(); - kdtree.add(&item2.0, item2.1).unwrap(); - kdtree.add(&item3.0, item3.1).unwrap(); - kdtree.add(&item4.0, item4.1).unwrap(); - - assert_eq!(kdtree.size(), 4); - let num_removed = kdtree.remove(&&[0f64], &1).unwrap(); - assert_eq!(kdtree.size(), 2); - assert_eq!(num_removed, 2); - assert_eq!( - kdtree.nearest(&[45f64], 1, &squared_euclidean).unwrap(), - vec![(0.0, &3)] - ); -} - -#[test] -fn handles_remove_no_match() { - let item1 = ([0f64], 1); - let item2 = ([100f64], 2); - let item3 = ([45f64], 3); - let item4 = ([55f64], 4); - - // Build a kd tree - let dimensions = 1; - let capacity_per_node = 2; - let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node); - - kdtree.add(&item1.0, item1.1).unwrap(); - kdtree.add(&item2.0, item2.1).unwrap(); - kdtree.add(&item3.0, item3.1).unwrap(); - kdtree.add(&item4.0, item4.1).unwrap(); - - let num_removed = kdtree.remove(&&[1f64], &2).unwrap(); - assert_eq!(kdtree.size(), 4); - assert_eq!(num_removed, 0); - assert_eq!( - kdtree.nearest(&[51f64], 2, &squared_euclidean).unwrap(), - vec![(16.0, &4), (36.0, &3)] - ); -} - -#[test] -fn handles_remove_overlapping_points() { - let a = ([0f64, 0f64], 0); - let b = ([0f64, 0f64], 1); - let mut kdtree = KdTree::new(2); - - kdtree.add(a.0, a.1).unwrap(); - kdtree.add(b.0, b.1).unwrap(); - - let num_removed = kdtree.remove(&[0f64, 0f64], &1).unwrap(); - assert_eq!(kdtree.size(), 1); - assert_eq!(num_removed, 1); - assert_eq!( - kdtree.nearest(&[0f64, 0f64], 1, &squared_euclidean).unwrap(), - vec![(0.0, &0)] - ); -} diff --git a/tests/serialization.rs b/tests/serde.rs similarity index 100% rename from tests/serialization.rs rename to tests/serde.rs