diff --git a/vrp-core/src/algorithms/structures/bitvec.rs b/vrp-core/src/algorithms/structures/bitvec.rs index d8b345974..85c3d6380 100644 --- a/vrp-core/src/algorithms/structures/bitvec.rs +++ b/vrp-core/src/algorithms/structures/bitvec.rs @@ -1,4 +1,9 @@ //! A tweaked version of `BitVec` from `probabilistic-collections` crate. + +#[cfg(test)] +#[path = "../../../tests/unit/algorithms/structures/bitvec_test.rs"] +mod bitvec_test; + use std::fmt::Display; use std::ops::Index; diff --git a/vrp-core/src/models/common/footprint.rs b/vrp-core/src/models/common/footprint.rs index ec606fa93..142c13359 100644 --- a/vrp-core/src/models/common/footprint.rs +++ b/vrp-core/src/models/common/footprint.rs @@ -1,5 +1,9 @@ //! This module contains a logic to maintain a low-dimensional representation of the VRP Solution. +#[cfg(test)] +#[path = "../../../tests/unit/models/common/footprint_test.rs"] +mod footprint_test; + use crate::algorithms::structures::BitVec; use crate::models::common::Location; use crate::prelude::*; diff --git a/vrp-core/tests/unit/algorithms/structures/bitvec_test.rs b/vrp-core/tests/unit/algorithms/structures/bitvec_test.rs new file mode 100644 index 000000000..2cd417ce4 --- /dev/null +++ b/vrp-core/tests/unit/algorithms/structures/bitvec_test.rs @@ -0,0 +1,58 @@ +use super::BitVec; + +#[test] +fn can_create_new() { + let bitvec = BitVec::new(10); + assert_eq!(bitvec.len(), 10); + assert!(bitvec.blocks.iter().all(|&block| block == 0)); +} + +#[test] +fn can_use_set_and_get() { + let mut bitvec = BitVec::new(10); + bitvec.set(3, true); + assert_eq!(bitvec.get(3), Some(true)); + bitvec.set(3, false); + assert_eq!(bitvec.get(3), Some(false)); +} + +#[test] +#[should_panic] +fn can_panic_when_set_out_of_bounds() { + let mut bitvec = BitVec::new(10); + bitvec.set(10, true); +} + +#[test] +fn can_use_union() { + let mut bitvec1 = BitVec::new(10); + let mut bitvec2 = BitVec::new(10); + bitvec1.set(3, true); + bitvec2.set(4, true); + bitvec1.union(&bitvec2); + assert_eq!(bitvec1.get(3), Some(true)); + assert_eq!(bitvec1.get(4), Some(true)); +} + +#[test] +fn can_use_len_and_is_empty() { + let bitvec = BitVec::new(0); + assert_eq!(bitvec.len(), 0); + assert!(bitvec.is_empty()); +} + +#[test] +fn can_index() { + let mut bitvec = BitVec::new(10); + bitvec.set(3, true); + assert_eq!(bitvec[3], true); + assert_eq!(bitvec[4], false); +} + +#[test] +fn can_use_display() { + let mut bitvec = BitVec::new(5); + bitvec.set(0, true); + bitvec.set(2, true); + assert_eq!(format!("{}", bitvec), "[10100]"); +} diff --git a/vrp-core/tests/unit/models/common/footprint_test.rs b/vrp-core/tests/unit/models/common/footprint_test.rs new file mode 100644 index 000000000..755055ac1 --- /dev/null +++ b/vrp-core/tests/unit/models/common/footprint_test.rs @@ -0,0 +1,175 @@ +use super::*; +use crate::algorithms::structures::BitVec; +use crate::helpers::construction::heuristics::TestInsertionContextBuilder; +use crate::helpers::models::domain::TestGoalContextBuilder; +use crate::helpers::models::problem::{TestSingleBuilder, TestVehicleBuilder}; +use crate::helpers::models::solution::{ActivityBuilder, RouteBuilder, RouteContextBuilder}; +use crate::models::problem::{create_matrix_transport_cost, MatrixData}; +use crate::prelude::*; + +fn create_test_problem() -> Problem { + ProblemBuilder::default() + .add_vehicle(TestVehicleBuilder::default().build()) + .add_jobs( + vec![ + TestSingleBuilder::default().id("job1").build_as_job_ref(), + TestSingleBuilder::default().id("job2").build_as_job_ref(), + ] + .into_iter(), + ) + .with_transport_cost( + create_matrix_transport_cost(vec![MatrixData::new(0, None, vec![0.; 9], vec![0.; 9])]).unwrap(), + ) + .with_goal(TestGoalContextBuilder::default().build()) + .build() + .unwrap() +} + +#[test] +fn can_use_footprint_new() { + let problem = create_test_problem(); + let footprint = Footprint::new(&problem); + assert_eq!(footprint.dimension(), get_dimension(&problem)); + assert!(footprint.repr.iter().all(|&value| value == 0)); +} + +#[test] +fn can_use_footprint_add() { + let problem = create_test_problem(); + let mut footprint = Footprint::new(&problem); + let mut shadow = Shadow { repr: BitVec::new(footprint.dimension() * footprint.dimension()) }; + shadow.repr.set(0, true); + shadow.repr.set(4, true); + + footprint.add(&shadow); + + assert_eq!(footprint.get(0, 0), 1); + assert_eq!(footprint.get(1, 1), 1); +} + +#[test] +fn can_use_footprint_union() { + let problem = create_test_problem(); + let mut footprint1 = Footprint::new(&problem); + let mut footprint2 = Footprint::new(&problem); + footprint1.repr[0] = 1; + footprint2.repr[1] = 1; + + footprint1.union(&footprint2); + + assert_eq!(footprint1.get(0, 0), 1); + assert_eq!(footprint1.get(0, 1), 1); +} + +#[test] +fn can_use_footprint_get() { + let problem = create_test_problem(); + let mut footprint = Footprint::new(&problem); + footprint.repr[0] = 2; + + assert_eq!(footprint.get(0, 0), 2); +} + +#[test] +fn tcan_use_footprint_iter() { + let problem = create_test_problem(); + let mut footprint = Footprint::new(&problem); + footprint.repr[0] = 1; + footprint.repr[1] = 2; + + #[rustfmt::skip] + let expected = vec![ + ((0, 0), 1), ((0, 1), 2), ((0, 2), 0), + ((1, 0), 0), ((1, 1), 0), ((1, 2), 0), + ((2, 0), 0), ((2, 1), 0), ((2, 2), 0) + ]; + + let result: Vec<_> = footprint.iter().collect(); + assert_eq!(result, expected); +} + +#[test] +fn can_use_footprint_forget() { + let problem = create_test_problem(); + let mut footprint = Footprint::new(&problem); + footprint.repr[0] = 16; + footprint.repr[1] = 4; + footprint.repr[2] = 0; + + footprint.forget(); + + assert_eq!(footprint.get(0, 0), 16_f64.log2() as u8); + assert_eq!(footprint.get(0, 1), 4_f64.log2() as u8); + assert_eq!(footprint.get(0, 2), 0); +} + +#[test] +fn can_use_footprint_estimate_solution() { + let problem = create_test_problem(); + let mut footprint = Footprint::new(&problem); + footprint.repr[1] = 1; // 0 -> 1 + footprint.repr[5] = 2; // 1 -> 2 + footprint.repr[6] = 3; // 2 -> 0 + let solution_ctx = TestInsertionContextBuilder::default() + .with_problem(problem) + .with_routes(vec![RouteContextBuilder::default() + .with_route( + RouteBuilder::default() + .add_activity(ActivityBuilder::with_location(1).build()) + .add_activity(ActivityBuilder::with_location(2).build()) + .build(), + ) + .build()]) + .build() + .solution; + + let cost = footprint.estimate_solution(&solution_ctx); + + assert_eq!(cost, 6); +} + +#[test] +fn can_use_footprint_estimate_edge() { + let problem = create_test_problem(); + let mut footprint = Footprint::new(&problem); + footprint.repr[0] = 3; + + let cost = footprint.estimate_edge(0, 0); + + assert_eq!(cost, 3); +} + +#[test] +fn can_create_shadow_new() { + let dim = 5; + let shadow = Shadow { repr: BitVec::new(dim * dim) }; + assert_eq!(shadow.dimension(), dim); +} + +#[test] +fn can_use_shadow_iter() { + let dim = 3; + let mut shadow = Shadow { repr: BitVec::new(dim * dim) }; + shadow.repr.set(0, true); + shadow.repr.set(4, true); + shadow.repr.set(8, true); + + #[rustfmt::skip] + let expected = vec![ + ((0, 0), true), ((0, 1), false), ((0, 2), false), + ((1, 0), false), ((1, 1), true), ((1, 2), false), + ((2, 0), false), ((2, 1), false), ((2, 2), true), + ]; + + let result: Vec<_> = shadow.iter().collect(); + assert_eq!(result, expected); +} + +#[test] +fn can_create_shadow_from_insertion_context() { + let problem = create_test_problem(); + let insertion_ctx = TestInsertionContextBuilder::default().with_problem(problem).build(); + + let shadow = Shadow::from(&insertion_ctx); + assert_eq!(shadow.dimension(), 3); +}