#[generic_tests::define]
mod t {
use super::super::*;
#[test]
fn set_aggregate_merges_siblings<P: Prefix + Copy + PartialEq>() {
let mut set = PrefixSet::<P>::from_iter([ip("10.0.0.0/24"), ip("10.0.1.0/24")]);
set.aggregate();
assert_eq!(Vec::from_iter(&set), vec![ip("10.0.0.0/23")]);
assert_eq!(set.len(), 1);
}
#[test]
fn set_aggregate_cascades_four_into_one<P: Prefix + Copy + PartialEq>() {
let mut set = PrefixSet::<P>::from_iter([
ip("10.0.0.0/24"),
ip("10.0.1.0/24"),
ip("10.0.2.0/24"),
ip("10.0.3.0/24"),
]);
set.aggregate();
assert_eq!(Vec::from_iter(&set), vec![ip("10.0.0.0/22")]);
assert_eq!(set.len(), 1);
}
#[test]
fn set_aggregate_drops_covered<P: Prefix + Copy + PartialEq>() {
let mut set =
PrefixSet::<P>::from_iter([ip("10.0.0.0/16"), ip("10.0.1.0/24"), ip("10.0.128.0/20")]);
set.aggregate();
assert_eq!(Vec::from_iter(&set), vec![ip("10.0.0.0/16")]);
assert_eq!(set.len(), 1);
}
#[test]
fn set_aggregate_merges_across_node_boundary<P: Prefix + Copy + PartialEq>() {
let mut set = PrefixSet::<P>::from_iter([ip("10.0.0.0/11"), ip("10.32.0.0/11")]);
set.aggregate();
assert_eq!(Vec::from_iter(&set), vec![ip("10.0.0.0/10")]);
assert_eq!(set.len(), 1);
}
#[test]
fn set_aggregate_keeps_unmergeable_siblings<P: Prefix + Copy + PartialEq>() {
let original = [ip("10.0.0.0/24"), ip("10.0.2.0/24"), ip("10.1.0.0/16")];
let mut set = PrefixSet::<P>::from_iter(original);
set.aggregate();
assert_eq!(Vec::from_iter(&set), Vec::from(original));
assert_eq!(set.len(), 3);
}
#[test]
fn set_aggregate_empty_is_noop<P: Prefix + Copy + PartialEq>() {
let mut set = PrefixSet::<P>::new();
set.aggregate();
assert!(set.is_empty());
}
#[test]
fn set_aggregate_collapses_to_default_route<P: Prefix + Copy + PartialEq>() {
let mut set = PrefixSet::<P>::from_iter([ip("0.0.0.0/1"), ip("128.0.0.0/1")]);
set.aggregate();
assert_eq!(Vec::from_iter(&set), vec![ip("0.0.0.0/0")]);
assert_eq!(set.len(), 1);
}
#[test]
fn set_aggregate_is_idempotent<P: Prefix + Copy + PartialEq>() {
let mut set = PrefixSet::<P>::from_iter([
ip("10.0.0.0/24"),
ip("10.0.1.0/24"),
ip("10.0.2.0/23"),
ip("10.0.4.0/23"),
ip("10.0.5.0/24"),
]);
set.aggregate();
let once = Vec::from_iter(&set);
set.aggregate();
assert_eq!(Vec::from_iter(&set), once);
}
#[test]
fn set_aggregate_consistent_drops_covered<P: Prefix + Copy + PartialEq>() {
let mut set = PrefixSet::<P>::from_iter([ip("10.0.0.0/16"), ip("10.0.1.0/24")]);
set.aggregate_consistent();
assert_eq!(Vec::from_iter(&set), vec![ip("10.0.0.0/16")]);
assert_eq!(set.len(), 1);
assert!(set.0.check_memory_alloc());
}
#[test]
fn set_aggregate_consistent_keeps_siblings<P: Prefix + Copy + PartialEq>() {
let original = [ip("10.0.0.0/24"), ip("10.0.1.0/24")];
let mut set = PrefixSet::<P>::from_iter(original);
set.aggregate_consistent();
assert_eq!(Vec::from_iter(&set), Vec::from(original));
assert_eq!(set.len(), 2);
assert!(set.0.check_memory_alloc());
}
#[test]
fn set_aggregate_consistent_drops_chain<P: Prefix + Copy + PartialEq>() {
let mut set =
PrefixSet::<P>::from_iter([ip("10.0.0.0/8"), ip("10.0.0.0/16"), ip("10.0.0.0/24")]);
set.aggregate_consistent();
assert_eq!(Vec::from_iter(&set), vec![ip("10.0.0.0/8")]);
assert_eq!(set.len(), 1);
}
#[test]
fn set_aggregate_consistent_drops_across_node_boundary<P: Prefix + Copy + PartialEq>() {
let mut set = PrefixSet::<P>::from_iter([ip("10.0.0.0/8"), ip("10.0.0.0/11")]);
set.aggregate_consistent();
assert_eq!(Vec::from_iter(&set), vec![ip("10.0.0.0/8")]);
assert_eq!(set.len(), 1);
assert!(set.0.check_memory_alloc());
}
#[test]
fn set_aggregate_consistent_keeps_uncovered<P: Prefix + Copy + PartialEq>() {
let original = [ip("10.0.0.0/24"), ip("10.0.2.0/24"), ip("10.1.0.0/16")];
let mut set = PrefixSet::<P>::from_iter(original);
set.aggregate_consistent();
assert_eq!(Vec::from_iter(&set), Vec::from(original));
assert_eq!(set.len(), 3);
}
#[test]
fn set_aggregate_consistent_empty_is_noop<P: Prefix + Copy + PartialEq>() {
let mut set = PrefixSet::<P>::new();
set.aggregate_consistent();
assert!(set.is_empty());
}
#[test]
fn set_aggregate_consistent_preserves_lpm_presence<P: Prefix + Copy + PartialEq>() {
let entries = [
ip("10.0.0.0/8"),
ip("10.0.0.0/16"),
ip("10.0.5.0/24"),
ip("10.0.8.0/23"),
ip("11.0.0.0/24"),
];
let original = PrefixSet::<P>::from_iter(entries);
let mut set = original.clone();
set.aggregate_consistent();
let probes = [
ip("10.0.0.0/8"),
ip("10.0.0.0/16"),
ip("10.0.5.0/24"),
ip("10.0.5.128/25"),
ip("10.0.8.0/23"),
ip("10.0.9.0/24"),
ip("11.0.0.0/24"),
ip("9.0.0.0/8"),
ip("12.0.0.0/8"),
];
for p in probes {
assert_eq!(
set.get_lpm(&p).is_some(),
original.get_lpm(&p).is_some(),
"lpm presence changed for {p:?}",
);
}
assert!(set.0.check_memory_alloc());
}
#[test]
fn set_aggregate_consistent_is_idempotent<P: Prefix + Copy + PartialEq>() {
let mut set = PrefixSet::<P>::from_iter([
ip("10.0.0.0/8"),
ip("10.0.0.0/16"),
ip("10.0.1.0/24"),
ip("10.1.0.0/16"),
]);
set.aggregate_consistent();
let once = Vec::from_iter(&set);
set.aggregate_consistent();
assert_eq!(Vec::from_iter(&set), once);
}
#[test]
fn map_aggregate_consistent_drops_same_value_descendant<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([(ip("10.0.0.0/16"), 1), (ip("10.0.1.0/24"), 1)]);
map.aggregate_consistent();
assert_eq!(Vec::from_iter(&map), vec![(ip("10.0.0.0/16"), &1)]);
assert_eq!(map.len(), 1);
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_consistent_keeps_diff_value_descendant<P: Prefix + Copy + PartialEq>() {
let original = [(ip("10.0.0.0/16"), 1), (ip("10.0.1.0/24"), 2)];
let mut map = Map::<P>::from_iter(original);
map.aggregate_consistent();
assert_eq!(
Vec::from_iter(&map),
vec![(ip("10.0.0.0/16"), &1), (ip("10.0.1.0/24"), &2)]
);
}
#[test]
fn map_aggregate_consistent_keeps_equal_siblings<P: Prefix + Copy + PartialEq>() {
let original = [(ip("10.0.0.0/24"), 1), (ip("10.0.1.0/24"), 1)];
let mut map = Map::<P>::from_iter(original);
map.aggregate_consistent();
assert_eq!(
Vec::from_iter(&map),
vec![(ip("10.0.0.0/24"), &1), (ip("10.0.1.0/24"), &1)]
);
assert_eq!(map.len(), 2);
}
#[test]
fn map_aggregate_consistent_drops_chain<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([
(ip("10.0.0.0/8"), 1),
(ip("10.0.0.0/16"), 1),
(ip("10.0.0.0/24"), 1),
]);
map.aggregate_consistent();
assert_eq!(Vec::from_iter(&map), vec![(ip("10.0.0.0/8"), &1)]);
assert_eq!(map.len(), 1);
}
#[test]
fn map_aggregate_consistent_chain_mixed_values<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([
(ip("10.0.0.0/8"), 1),
(ip("10.0.0.0/16"), 2),
(ip("10.0.0.0/24"), 2),
]);
map.aggregate_consistent();
assert_eq!(
Vec::from_iter(&map),
vec![(ip("10.0.0.0/8"), &1), (ip("10.0.0.0/16"), &2)]
);
assert_eq!(map.len(), 2);
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_consistent_across_node_boundary<P: Prefix + Copy + PartialEq>() {
let mut same = Map::<P>::from_iter([(ip("10.0.0.0/8"), 5), (ip("10.0.0.0/11"), 5)]);
same.aggregate_consistent();
assert_eq!(Vec::from_iter(&same), vec![(ip("10.0.0.0/8"), &5)]);
assert!(same.check_memory_alloc());
let mut diff = Map::<P>::from_iter([(ip("10.0.0.0/8"), 5), (ip("10.0.0.0/11"), 6)]);
diff.aggregate_consistent();
assert_eq!(
Vec::from_iter(&diff),
vec![(ip("10.0.0.0/8"), &5), (ip("10.0.0.0/11"), &6)]
);
}
#[test]
fn map_aggregate_consistent_empty_is_noop<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::new();
map.aggregate_consistent();
assert!(map.is_empty());
}
#[test]
fn map_aggregate_consistent_preserves_lpm<P: Prefix + Copy + PartialEq>() {
let entries = [
(ip("10.0.0.0/8"), 1),
(ip("10.0.0.0/16"), 1), (ip("10.0.5.0/24"), 2), (ip("10.0.8.0/23"), 1), (ip("11.0.0.0/24"), 3),
];
let original = Map::<P>::from_iter(entries);
let mut map = original.clone();
map.aggregate_consistent();
let probes = [
ip("10.0.0.0/8"),
ip("10.0.0.0/16"),
ip("10.0.5.0/24"),
ip("10.0.5.128/25"),
ip("10.0.8.0/23"),
ip("10.0.9.0/24"),
ip("11.0.0.0/24"),
ip("10.255.255.255/32"),
ip("9.0.0.0/32"),
ip("12.0.0.0/32"),
];
for p in probes {
assert_eq!(
map.get_lpm(&p).map(|(_, v)| *v),
original.get_lpm(&p).map(|(_, v)| *v),
"lpm value changed for {p:?}",
);
}
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_consistent_is_idempotent<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([
(ip("10.0.0.0/8"), 1),
(ip("10.0.0.0/16"), 1),
(ip("10.0.1.0/24"), 2),
(ip("10.1.0.0/16"), 3),
]);
map.aggregate_consistent();
let once = Vec::from_iter(map.iter().map(|(p, v)| (p, *v)));
map.aggregate_consistent();
assert_eq!(Vec::from_iter(map.iter().map(|(p, v)| (p, *v))), once);
}
#[test]
fn map_aggregate_merges_equal_value_siblings<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([(ip("10.0.0.0/24"), 1u32), (ip("10.0.1.0/24"), 1)]);
map.aggregate();
assert_eq!(
Vec::from_iter(map.iter().map(|(p, v)| (p, *v))),
vec![(ip("10.0.0.0/23"), 1)]
);
assert_eq!(map.len(), 1);
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_keeps_diff_value_siblings<P: Prefix + Copy + PartialEq>() {
let original = [(ip("10.0.0.0/24"), 1u32), (ip("10.0.1.0/24"), 2)];
let mut map = Map::<P>::from_iter(original);
map.aggregate();
assert_eq!(
Vec::from_iter(map.iter().map(|(p, v)| (p, *v))),
vec![(ip("10.0.0.0/23"), 1), (ip("10.0.1.0/24"), 2)]
);
}
#[test]
fn map_aggregate_keeps_hole<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([(ip("10.0.0.0/24"), 1u32)]);
map.aggregate();
assert_eq!(
Vec::from_iter(map.iter().map(|(p, v)| (p, *v))),
vec![(ip("10.0.0.0/24"), 1)]
);
assert_eq!(map.get_lpm(&ip("10.0.1.0/32")), None);
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_drops_covered_same_value<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([(ip("10.0.0.0/8"), 1u32), (ip("10.0.0.0/16"), 1)]);
map.aggregate();
assert_eq!(
Vec::from_iter(map.iter().map(|(p, v)| (p, *v))),
vec![(ip("10.0.0.0/8"), 1)]
);
assert_eq!(map.len(), 1);
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_keeps_diff_value_descendant<P: Prefix + Copy + PartialEq>() {
let original = [(ip("10.0.0.0/8"), 1u32), (ip("10.0.0.0/16"), 2)];
let mut map = Map::<P>::from_iter(original);
map.aggregate();
assert_eq!(
Vec::from_iter(map.iter().map(|(p, v)| (p, *v))),
vec![(ip("10.0.0.0/8"), 1), (ip("10.0.0.0/16"), 2)]
);
}
#[test]
fn map_aggregate_merges_across_node_boundary<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([(ip("10.0.0.0/11"), 1u32), (ip("10.32.0.0/11"), 1)]);
map.aggregate();
assert_eq!(
Vec::from_iter(map.iter().map(|(p, v)| (p, *v))),
vec![(ip("10.0.0.0/10"), 1)]
);
assert_eq!(map.len(), 1);
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_cascades<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([
(ip("10.0.0.0/25"), 1u32),
(ip("10.0.0.128/25"), 1),
(ip("10.0.1.0/25"), 1),
(ip("10.0.1.128/25"), 1),
]);
map.aggregate();
assert_eq!(
Vec::from_iter(map.iter().map(|(p, v)| (p, *v))),
vec![(ip("10.0.0.0/23"), 1)]
);
assert_eq!(map.len(), 1);
}
#[test]
fn map_aggregate_merges_with_exception<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([
(ip("10.0.0.0/24"), 1u32),
(ip("10.0.1.0/25"), 1),
(ip("10.0.1.128/25"), 2),
]);
map.aggregate();
assert_eq!(
Vec::from_iter(map.iter().map(|(p, v)| (p, *v))),
vec![(ip("10.0.0.0/23"), 1), (ip("10.0.1.128/25"), 2)]
);
assert_eq!(map.len(), 2);
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_empty_is_noop<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::new();
map.aggregate();
assert!(map.is_empty());
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_preserves_lpm<P: Prefix + Copy + PartialEq>() {
let entries = [
(ip("10.0.0.0/8"), 1u32),
(ip("10.0.0.0/16"), 1),
(ip("10.0.5.0/24"), 2),
(ip("10.0.8.0/23"), 1),
(ip("11.0.0.0/24"), 3),
];
let original = Map::<P>::from_iter(entries);
let mut map = original.clone();
map.aggregate();
let probes = [
ip("10.0.0.0/8"),
ip("10.0.0.0/16"),
ip("10.0.5.0/24"),
ip("10.0.5.128/25"),
ip("10.0.8.0/23"),
ip("11.0.0.0/24"),
ip("10.255.255.255/32"),
ip("9.0.0.0/32"),
];
for p in probes {
assert_eq!(
map.get_lpm(&p).map(|(_, v)| *v),
original.get_lpm(&p).map(|(_, v)| *v),
"lpm changed for {p:?}",
);
}
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_is_idempotent<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([
(ip("10.0.0.0/24"), 1u32),
(ip("10.0.1.0/24"), 1),
(ip("10.0.2.0/24"), 2),
(ip("10.0.3.0/24"), 2),
(ip("10.1.0.0/16"), 1),
]);
map.aggregate();
let once = Vec::from_iter(map.iter().map(|(p, v)| (p, *v)));
map.aggregate();
assert_eq!(Vec::from_iter(map.iter().map(|(p, v)| (p, *v))), once);
}
#[test]
fn map_aggregate_fill_empty_makes_default_route<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::new();
map.aggregate_fill(|| 5);
assert_eq!(
Vec::from_iter(map.iter().map(|(p, v)| (p, *v))),
vec![(ip("0.0.0.0/0"), 5)]
);
assert_eq!(map.len(), 1);
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_fill_adds_default_over_covered<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([(ip("10.0.0.0/8"), 1u32)]);
map.aggregate_fill(|| 9);
assert_eq!(
Vec::from_iter(map.iter().map(|(p, v)| (p, *v))),
vec![(ip("0.0.0.0/0"), 9), (ip("10.0.0.0/8"), 1)]
);
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_fill_collapses_when_default_matches<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([(ip("10.0.0.0/8"), 1u32), (ip("192.168.0.0/16"), 1)]);
map.aggregate_fill(|| 1);
assert_eq!(
Vec::from_iter(map.iter().map(|(p, v)| (p, *v))),
vec![(ip("0.0.0.0/0"), 1)]
);
assert_eq!(map.len(), 1);
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_fill_merge_and_collapse<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([
(ip("10.0.0.0/16"), 1u32),
(ip("10.0.0.0/24"), 2),
(ip("10.0.1.0/24"), 2),
(ip("10.0.2.0/24"), 1),
(ip("192.168.0.0/16"), 1),
]);
map.aggregate_fill(|| 1);
assert_eq!(
Vec::from_iter(map.iter().map(|(p, v)| (p, *v))),
vec![(ip("0.0.0.0/0"), 1), (ip("10.0.0.0/23"), 2)]
);
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_fill_keeps_distinct_values<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([
(ip("10.0.0.0/30"), 1u32),
(ip("10.0.1.0/24"), 2),
(ip("10.1.0.0/16"), 3),
]);
map.aggregate_fill_default();
assert_eq!(
Vec::from_iter(map.iter().map(|(p, v)| (p, *v))),
vec![
(ip("0.0.0.0/0"), 0),
(ip("10.0.0.0/30"), 1),
(ip("10.0.1.0/24"), 2),
(ip("10.1.0.0/16"), 3),
]
);
assert!(map.check_memory_alloc());
}
#[test]
fn map_aggregate_fill_is_idempotent<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([
(ip("10.0.0.0/24"), 1u32),
(ip("10.0.1.0/24"), 2),
(ip("10.1.0.0/16"), 3),
]);
map.aggregate_fill_default();
let once = Vec::from_iter(map.iter().map(|(p, v)| (p, *v)));
map.aggregate_fill_default();
assert_eq!(Vec::from_iter(map.iter().map(|(p, v)| (p, *v))), once);
}
#[test]
fn map_aggregate_fill_default_uses_zero<P: Prefix + Copy + PartialEq>() {
let mut map = Map::<P>::from_iter([(ip("10.0.0.0/8"), 1u32)]);
map.aggregate_fill_default();
assert_eq!(
Vec::from_iter(map.iter().map(|(p, v)| (p, *v))),
vec![(ip("0.0.0.0/0"), 0), (ip("10.0.0.0/8"), 1)]
);
assert!(map.check_memory_alloc());
}
#[instantiate_tests(<(u32, u8)>)]
mod raw32 {}
#[instantiate_tests(<(u64, u8)>)]
mod raw64 {}
#[instantiate_tests(<(u128, u8)>)]
mod raw128 {}
#[cfg(feature = "ipnet")]
#[instantiate_tests(<ipnet::Ipv4Net>)]
mod ipv4net {}
#[cfg(feature = "ipnet")]
#[instantiate_tests(<ipnet::Ipv6Net>)]
mod ipv6net {}
#[cfg(feature = "ipnetwork")]
#[instantiate_tests(<ipnetwork::Ipv4Network>)]
mod ipv4network {}
#[cfg(feature = "ipnetwork")]
#[instantiate_tests(<ipnetwork::Ipv6Network>)]
mod ipv6network {}
#[cfg(feature = "cidr")]
#[instantiate_tests(<cidr::Ipv4Cidr>)]
mod ipv4cidr {}
#[cfg(feature = "cidr")]
#[instantiate_tests(<cidr::Ipv6Cidr>)]
mod ipv6cidr {}
#[cfg(feature = "cidr")]
#[instantiate_tests(<cidr::Ipv4Inet>)]
mod ipv4inet {}
#[cfg(feature = "cidr")]
#[instantiate_tests(<cidr::Ipv6Inet>)]
mod ipv6inet {}
}