diff --git a/src/maps.rs b/src/maps.rs index 7c01d8a..136b815 100644 --- a/src/maps.rs +++ b/src/maps.rs @@ -142,6 +142,7 @@ impl IndexMut for ElementMap { #[cfg(test)] mod tests { use super::*; + use std::rc::Rc; #[test] fn initial_values_are_default() { @@ -238,4 +239,143 @@ mod tests { "slot added by grow should hold the mapped default" ); } + + #[test] + fn remap() { + let mut source = ElementMap::new('-', |e| e, 3); + source[0] = 'a'; + source[1] = 'b'; + source[2] = 'c'; + let mut mapping = ElementMap::new(usize::MAX, |e| e, 3); + mapping[0] = 2; + mapping[1] = 0; + mapping[2] = 1; + let template = ElementMap::new((), |f| f, 3); + let result = source.remap(&mapping, &template); + assert_eq!(result[0], 'b', "unexpected value at slot 0"); + assert_eq!(result[1], 'c', "unexpected value at slot 1"); + assert_eq!(result[2], 'a', "unexpected value at slot 2"); + assert_eq!( + result.default, '-', + "default should be taken from the source map" + ); + } + + #[test] + fn remap_skips_default() { + let mut source = ElementMap::new(0, |e| e, 3); + source[0] = 1; + source[1] = 2; + source[2] = 3; + // Slot 1 stays unmapped, which would be out of bounds for the template if it was used. + let mut mapping = ElementMap::new(usize::MAX, |e| e, 3); + mapping[0] = 1; + mapping[2] = 0; + let template = ElementMap::new((), |f| f, 2); + let result = source.remap(&mapping, &template); + assert_eq!(result[0], 3, "unexpected value at slot 0"); + assert_eq!(result[1], 1, "unexpected value at slot 1"); + } + + #[test] + fn remap_unmapped_slots() { + let mut source = ElementMap::new(9, |e| e, 2); + source[0] = 1; + source[1] = 2; + let mut mapping = ElementMap::new(usize::MAX, |e| e, 2); + mapping[0] = 0; + mapping[1] = 2; + let template = ElementMap::new((), |f| f, 4); + let result = source.remap(&mapping, &template); + assert_eq!( + result.data, + vec![1, 9, 2, 9], + "should have the template's length, with unmapped slots holding the source's default" + ); + } + + #[test] + fn remap_index_function() { + let mut source = ElementMap::::new(0, |e| e, 3); + source[0] = 10; + source[1] = 20; + source[2] = 30; + let mut mapping = ElementMap::::new(u32::MAX, |e| e, 3); + mapping[0] = 0; + mapping[1] = 1; + mapping[2] = 2; + // Reverses the order of the slots. + let template = ElementMap::::new((), |f| 2 - f as usize, 3); + let result = source.remap(&mapping, &template); + assert_eq!( + result.data, + vec![30, 20, 10], + "values should be written at the slots given by the template's index function" + ); + assert_eq!( + (result.to_index)(0), + 2, + "index function should be taken from the template" + ); + } + + #[test] + fn remap_shorter_source() { + let mut source = ElementMap::new(9, |e| e, 2); + source[0] = 5; + source[1] = 6; + let mut mapping = ElementMap::new(usize::MAX, |e| e, 4); + for i in 0..4 { + mapping[i] = i; + } + let template = ElementMap::new((), |f| f, 4); + let result = source.remap(&mapping, &template); + assert_eq!(result.data, vec![5, 6, 9, 9], "unexpected data"); + } + + #[test] + fn remap_longer_source() { + let mut source = ElementMap::new(0, |e| e, 4); + for i in 0..4 { + source[i] = i + 1; + } + let mut mapping = ElementMap::new(usize::MAX, |e| e, 2); + mapping[0] = 1; + mapping[1] = 0; + let template = ElementMap::new((), |f| f, 2); + let result = source.remap(&mapping, &template); + assert_eq!(result.data, vec![2, 1], "unexpected data"); + } + + #[test] + fn remap_empty() { + let source = ElementMap::new(0, |e| e, 0); + let mapping = ElementMap::new(usize::MAX, |e| e, 0); + let template = ElementMap::new((), |f| f, 0); + let result = source.remap(&mapping, &template); + assert_eq!(result.data, Vec::::new(), "should be empty"); + } + + #[test] + fn remap_clones_values() { + let value = Rc::new(1); + let mut source = ElementMap::new(Rc::new(0), |e| e, 1); + source[0] = Rc::clone(&value); + let mut mapping = ElementMap::new(usize::MAX, |e| e, 1); + mapping[0] = 0; + let template = ElementMap::new((), |f| f, 1); + let _result = source.remap(&mapping, &template); + // The local variable, the source slot and the result slot each hold one reference. + assert_eq!( + Rc::strong_count(&value), + 3, + "result should hold its own clone of the value" + ); + drop(source); + assert_eq!( + Rc::strong_count(&value), + 2, + "result should keep the value alive on its own" + ); + } }