From 7f187feb6cb79eca7906a84a457401e8e7a91400 Mon Sep 17 00:00:00 2001
From: Tiram <18632023+tiram88@users.noreply.github.com>
Date: Mon, 15 Apr 2024 17:39:57 +0000
Subject: [PATCH] Add unit tests covering `CounterMap` and `IndexSet` tracking
---
notify/benches/bench.rs | 2 +-
notify/src/address/tracker.rs | 171 ++++++++++++++++++++++++++++++++--
2 files changed, 164 insertions(+), 9 deletions(-)
diff --git a/notify/benches/bench.rs b/notify/benches/bench.rs
index e9dacc36f..5317eeb49 100644
--- a/notify/benches/bench.rs
+++ b/notify/benches/bench.rs
@@ -11,7 +11,7 @@ fn create_addresses(count: usize) -> Vec
{
fn create_and_fill_context(addresses: Vec) -> SubscriptionContext {
let context = SubscriptionContext::with_options(Some(ADDRESS_COUNT));
- let mut indexes = Indexes::new(context.address_tracker.clone(), vec![]);
+ let mut indexes = Indexes::new(context.address_tracker.clone());
let _ = indexes.register(addresses);
context
}
diff --git a/notify/src/address/tracker.rs b/notify/src/address/tracker.rs
index f4a34fde3..a2261cfaa 100644
--- a/notify/src/address/tracker.rs
+++ b/notify/src/address/tracker.rs
@@ -119,6 +119,11 @@ impl CounterMap {
pub fn test_direct_insert(&mut self, index: Index) -> bool {
self.insert(index)
}
+
+ #[cfg(test)]
+ fn test_get_address(&self, address: &Address) -> Option {
+ self.tracker.get_address(address).and_then(|(index, _)| self.indexes.get(&index).copied())
+ }
}
impl Indexer for CounterMap {
@@ -208,8 +213,8 @@ pub struct IndexSet {
}
impl IndexSet {
- pub fn new(tracker: Tracker, indexes: Vec) -> Self {
- Self { tracker, indexes: indexes.into_iter().collect() }
+ pub fn new(tracker: Tracker) -> Self {
+ Self { tracker, indexes: HashSet::new() }
}
pub fn with_capacity(tracker: Tracker, capacity: usize) -> Self {
@@ -254,10 +259,21 @@ impl IndexSet {
self.indexes.capacity()
}
+ #[cfg(test)]
+ pub fn test_with_indexes(tracker: Tracker, indexes: Vec) -> Self {
+ tracker.reference_indexes(indexes.iter());
+ Self { tracker, indexes: indexes.into_iter().collect() }
+ }
+
#[cfg(test)]
pub fn test_direct_insert(&mut self, index: Index) -> bool {
self.insert(index)
}
+
+ #[cfg(test)]
+ fn test_get_address(&self, address: &Address) -> bool {
+ self.tracker.get_address(address).and_then(|(index, _)| self.indexes.get(&index)).is_some()
+ }
}
impl Indexer for IndexSet {
@@ -671,6 +687,7 @@ impl Tracker {
}
});
}
+ counters.empty_entries = 0;
}
pub fn to_addresses(&self, indexes: &[Index], prefix: Prefix) -> Vec {
@@ -755,7 +772,7 @@ mod tests {
assert_eq!(aa.len(), MAX_ADDRESSES);
// Register addresses 0..MAX_ADDRESSES
- let mut idx_a = Indexes::new(tracker.clone(), vec![]);
+ let mut idx_a = Indexes::new(tracker.clone());
let aa = idx_a.register(aa).unwrap();
let aai = aa.iter().map(|x| tracker.get_address(x).unwrap().0).collect_vec();
assert_eq!(aa.len(), MAX_ADDRESSES, "all addresses should be registered");
@@ -776,7 +793,7 @@ mod tests {
// Register address set 1..MAX_ADDRESSES, already fully covered by the tracker address set
const AB_COUNT: usize = MAX_ADDRESSES - 1;
- let mut idx_b = Indexes::new(tracker.clone(), vec![]);
+ let mut idx_b = Indexes::new(tracker.clone());
let ab = idx_b.register(create_addresses(1, AB_COUNT)).unwrap();
assert_eq!(ab.len(), AB_COUNT, "all addresses should be registered");
assert_eq!(idx_b.len(), AB_COUNT, "all addresses should be registered");
@@ -805,10 +822,10 @@ mod tests {
#[test]
fn test_indexes_eq() {
let tracker = Tracker::new(None);
- let i1 = IndexSet::new(tracker.clone(), vec![0, 1, 2, 3, 5, 7, 11]);
- let i2 = IndexSet::new(tracker.clone(), vec![5, 7, 11, 0, 1, 2, 3]);
- let i3 = IndexSet::new(tracker.clone(), vec![0, 1, 2, 4, 8, 16, 32]);
- let i4 = IndexSet::new(tracker.clone(), vec![0, 1]);
+ let i1 = IndexSet::test_with_indexes(tracker.clone(), vec![0, 1, 2, 3, 5, 7, 11]);
+ let i2 = IndexSet::test_with_indexes(tracker.clone(), vec![5, 7, 11, 0, 1, 2, 3]);
+ let i3 = IndexSet::test_with_indexes(tracker.clone(), vec![0, 1, 2, 4, 8, 16, 32]);
+ let i4 = IndexSet::test_with_indexes(tracker.clone(), vec![0, 1]);
assert_eq!(i1, i1);
assert_eq!(i1, i2);
assert_ne!(i1, i3);
@@ -851,4 +868,142 @@ mod tests {
m.insert(CAPACITY as u64 + 1, 0);
assert_eq!(m.capacity(), ((CAPACITY + 1) * 8 / 7).next_power_of_two() * 7 / 8);
}
+
+ #[test]
+ fn test_counter_map_tracking() {
+ struct Test {
+ tracker: Tracker,
+ addresses: Vec,
+ }
+
+ impl Test {
+ fn assert(&self, label: &str, item: &CounterMap, item_counters: &[RefCount], tracker_counters: &[RefCount]) {
+ self.assert_tracker(label, tracker_counters);
+ self.assert_counter_map(label, item, item_counters)
+ }
+
+ fn assert_tracker(&self, label: &str, counters: &[RefCount]) {
+ assert_eq!(self.tracker.len(), counters.iter().filter(|x| **x > 0).count(), "{}: length should match", label);
+ let tracker_counters =
+ (0..counters.len()).map(|i| self.tracker.get_address(&self.addresses[i]).unwrap().1).collect_vec();
+ assert_eq!(tracker_counters, *counters, "{}: counters should match", label);
+ }
+
+ fn assert_counter_map(&self, label: &str, item: &CounterMap, counters: &[RefCount]) {
+ assert_eq!(item.len(), counters.iter().filter(|x| **x > 0).count(), "{}: length should match", label);
+ let item_counters =
+ (0..counters.len()).map(|i| item.test_get_address(&self.addresses[i]).unwrap_or_default()).collect_vec();
+ assert_eq!(item_counters, *counters, "{}: counters should match", label);
+ }
+ }
+
+ let tracker = Tracker::new(None);
+ let addresses = create_addresses(0, 3);
+ let test = Test { tracker: tracker.clone(), addresses };
+
+ let mut c1 = CounterMap::new(tracker.clone());
+ test.assert("c1: new", &c1, &[], &[]);
+ assert_eq!(c1.register(test.addresses.clone()).unwrap(), test.addresses);
+ test.assert("c1: register [0, 1 ,2]", &c1, &[1, 1, 1], &[1, 1, 1]);
+ assert_eq!(c1.register(test.addresses[0..=1].to_vec()).unwrap(), vec![]);
+ test.assert("c1: register [0, 1]", &c1, &[2, 2, 1], &[1, 1, 1]);
+
+ let mut c2 = c1.clone();
+ test.assert("c2: clone c1", &c2, &[2, 2, 1], &[2, 2, 2]);
+
+ let mut c3 = c1.clone();
+ test.assert("c3: clone c1", &c3, &[2, 2, 1], &[3, 3, 3]);
+ c3.clear();
+ test.assert("c3: clear", &c3, &[0, 0, 0], &[2, 2, 2]);
+ drop(c3);
+ test.assert_tracker("c3: drop", &[2, 2, 2]);
+
+ assert_eq!(c2.unregister(test.addresses[1..=2].to_vec()), test.addresses[2..=2].to_vec());
+ test.assert("c2: unregister [1, 2]", &c2, &[2, 1, 0], &[2, 2, 1]);
+ assert_eq!(c2.unregister(test.addresses[0..=1].to_vec()), test.addresses[1..=1].to_vec());
+ test.assert("c2: unregister [0, 1]", &c2, &[1, 0, 0], &[2, 1, 1]);
+ assert_eq!(c2.unregister(test.addresses[0..=0].to_vec()), test.addresses[0..=0].to_vec());
+ test.assert("c2: unregister [0]", &c2, &[], &[1, 1, 1]);
+ drop(c2);
+ test.assert_tracker("c2: drop", &[1, 1, 1]);
+
+ assert_eq!(c1.unregister(test.addresses[0..=0].to_vec()), vec![]);
+ test.assert("c1: unregister [0]", &c1, &[1, 2, 1], &[1, 1, 1]);
+ assert_eq!(c1.unregister(test.addresses[0..=1].to_vec()), test.addresses[0..=0].to_vec());
+ test.assert("c1: unregister [0, 1]", &c1, &[0, 1, 1], &[0, 1, 1]);
+ assert_eq!(c1.unregister(test.addresses[1..=2].to_vec()), test.addresses[1..=2].to_vec());
+ test.assert("c1: unregister [1, 2]", &c1, &[0, 0, 0], &[0, 0, 0]);
+
+ drop(c1);
+ test.assert_tracker("c1: drop", &[0, 0, 0]);
+ }
+
+ #[test]
+ fn test_index_set_tracking() {
+ struct Test {
+ tracker: Tracker,
+ addresses: Vec,
+ }
+
+ impl Test {
+ fn assert(&self, label: &str, item: &IndexSet, item_bits: &[RefCount], tracker_counters: &[RefCount]) {
+ self.assert_tracker(label, tracker_counters);
+ self.assert_index_set(label, item, item_bits)
+ }
+
+ fn assert_tracker(&self, label: &str, counters: &[RefCount]) {
+ assert_eq!(self.tracker.len(), counters.iter().filter(|x| **x > 0).count(), "{}: length should match", label);
+ let tracker_counters =
+ (0..counters.len()).map(|i| self.tracker.get_address(&self.addresses[i]).unwrap().1).collect_vec();
+ assert_eq!(tracker_counters, *counters, "{}: counters should match", label);
+ }
+
+ fn assert_index_set(&self, label: &str, item: &IndexSet, bits: &[RefCount]) {
+ assert_eq!(item.len(), bits.iter().filter(|x| **x > 0).count(), "{}: length should match", label);
+ let item_counters =
+ (0..bits.len()).map(|i| if item.test_get_address(&self.addresses[i]) { 1 } else { 0 }).collect_vec();
+ assert_eq!(item_counters, *bits, "{}: counters should match", label);
+ }
+ }
+
+ let tracker = Tracker::new(None);
+ let addresses = create_addresses(0, 3);
+ let test = Test { tracker: tracker.clone(), addresses };
+
+ let mut c1 = IndexSet::new(tracker.clone());
+ test.assert("c1: new", &c1, &[], &[]);
+ assert_eq!(c1.register(test.addresses.clone()).unwrap(), test.addresses);
+ test.assert("c1: register [0, 1 ,2]", &c1, &[1, 1, 1], &[1, 1, 1]);
+ assert_eq!(c1.register(test.addresses[0..=1].to_vec()).unwrap(), vec![]);
+ test.assert("c1: register [0, 1]", &c1, &[1, 1, 1], &[1, 1, 1]);
+
+ let mut c2 = c1.clone();
+ test.assert("c2: clone c1", &c2, &[1, 1, 1], &[2, 2, 2]);
+
+ let mut c3 = c1.clone();
+ test.assert("c3: clone c1", &c3, &[1, 1, 1], &[3, 3, 3]);
+ c3.clear();
+ test.assert("c3: clear", &c3, &[0, 0, 0], &[2, 2, 2]);
+ drop(c3);
+ test.assert_tracker("c3: drop", &[2, 2, 2]);
+
+ assert_eq!(c2.unregister(test.addresses[1..=2].to_vec()), test.addresses[1..=2].to_vec());
+ test.assert("c2: unregister [1, 2]", &c2, &[1, 0, 0], &[2, 1, 1]);
+ assert_eq!(c2.unregister(test.addresses[0..=1].to_vec()), test.addresses[0..=0].to_vec());
+ test.assert("c2: unregister [0, 1]", &c2, &[0, 0, 0], &[1, 1, 1]);
+ assert_eq!(c2.unregister(test.addresses[0..=0].to_vec()), vec![]);
+ test.assert("c2: unregister [0]", &c2, &[], &[1, 1, 1]);
+ drop(c2);
+ test.assert_tracker("c2: drop", &[1, 1, 1]);
+
+ assert_eq!(c1.unregister(test.addresses[0..=0].to_vec()), test.addresses[0..=0].to_vec());
+ test.assert("c1: unregister [0]", &c1, &[0, 1, 1], &[0, 1, 1]);
+ assert_eq!(c1.unregister(test.addresses[0..=1].to_vec()), test.addresses[1..=1].to_vec());
+ test.assert("c1: unregister [0, 1]", &c1, &[0, 0, 1], &[0, 0, 1]);
+ assert_eq!(c1.unregister(test.addresses[1..=2].to_vec()), test.addresses[2..=2].to_vec());
+ test.assert("c1: unregister [1, 2]", &c1, &[0, 0, 0], &[0, 0, 0]);
+
+ drop(c1);
+ test.assert_tracker("c1: drop", &[0, 0, 0]);
+ }
}