diff --git a/veilid-core/src/routing_table/mod.rs b/veilid-core/src/routing_table/mod.rs index 04e21ee5..3665c3c9 100644 --- a/veilid-core/src/routing_table/mod.rs +++ b/veilid-core/src/routing_table/mod.rs @@ -13,6 +13,8 @@ mod stats_accounting; mod tasks; mod types; +pub mod tests; + use super::*; use crate::crypto::*; @@ -290,8 +292,8 @@ impl RoutingTable { debug!("finished routing table terminate"); } - /// Serialize routing table to table store - async fn save_buckets(&self) -> EyreResult<()> { + /// Serialize the routing table. + fn serialized_buckets(&self) -> EyreResult<(BTreeMap>>, Vec>)> { // Since entries are shared by multiple buckets per cryptokind // we need to get the list of all unique entries when serializing let mut all_entries: Vec> = Vec::new(); @@ -319,6 +321,13 @@ impl RoutingTable { all_entry_bytes.push(entry_bytes); } + Ok((serialized_bucket_map, all_entry_bytes)) + } + + /// Write the serialized routing table to the table store. + async fn save_buckets(&self) -> EyreResult<()> { + let (serialized_bucket_map, all_entry_bytes) = self.serialized_buckets()?; + let table_store = self.unlocked_inner.network_manager().table_store(); let tdb = table_store.open("routing_table", 1).await?; let dbx = tdb.transact(); @@ -333,7 +342,6 @@ impl RoutingTable { dbx.commit().await?; Ok(()) } - /// Deserialize routing table from table store async fn load_buckets(&self) -> EyreResult<()> { // Deserialize bucket map and all entries from the table store @@ -350,7 +358,18 @@ impl RoutingTable { // Reconstruct all entries let inner = &mut *self.inner.write(); + self.populate_routing_table(inner, serialized_bucket_map, all_entry_bytes)?; + Ok(()) + } + + /// Write the deserialized table store data to the routing table. + pub fn populate_routing_table( + &self, + inner: &mut RoutingTableInner, + serialized_bucket_map: BTreeMap>>, + all_entry_bytes: Vec>, + ) -> EyreResult<()> { let mut all_entries: Vec> = Vec::with_capacity(all_entry_bytes.len()); for entry_bytes in all_entry_bytes { let entryinner = diff --git a/veilid-core/src/routing_table/tests/mod.rs b/veilid-core/src/routing_table/tests/mod.rs new file mode 100644 index 00000000..8205daa2 --- /dev/null +++ b/veilid-core/src/routing_table/tests/mod.rs @@ -0,0 +1,3 @@ +pub mod test_serialize; + +use super::*; diff --git a/veilid-core/src/routing_table/tests/test_serialize.rs b/veilid-core/src/routing_table/tests/test_serialize.rs new file mode 100644 index 00000000..14df616c --- /dev/null +++ b/veilid-core/src/routing_table/tests/test_serialize.rs @@ -0,0 +1,80 @@ +use crate::*; + +fn fake_routing_table() -> routing_table::RoutingTable { + let veilid_config = VeilidConfig::new(); + let block_store = BlockStore::new(veilid_config.clone()); + let protected_store = ProtectedStore::new(veilid_config.clone()); + let table_store = TableStore::new(veilid_config.clone()); + let crypto = Crypto::new( + veilid_config.clone(), + table_store.clone(), + protected_store.clone(), + ); + let storage_manager = storage_manager::StorageManager::new( + veilid_config.clone(), + crypto.clone(), + protected_store.clone(), + table_store.clone(), + block_store.clone(), + ); + let network_manager = network_manager::NetworkManager::new( + veilid_config.clone(), + storage_manager, + protected_store.clone(), + table_store.clone(), + block_store.clone(), + crypto.clone(), + ); + routing_table::RoutingTable::new(network_manager) +} + +pub async fn test_routingtable_buckets_round_trip() { + let original = fake_routing_table(); + let copy = fake_routing_table(); + original.init().await.unwrap(); + copy.init().await.unwrap(); + + // Add lots of routes to `original` here to exercise all various types. + + let (serialized_bucket_map, all_entry_bytes) = original.serialized_buckets().unwrap(); + + copy.populate_routing_table( + &mut copy.inner.write(), + serialized_bucket_map, + all_entry_bytes, + ) + .unwrap(); + + let original_inner = &*original.inner.read(); + let copy_inner = &*copy.inner.read(); + + let routing_table_keys: Vec<_> = original_inner.buckets.keys().clone().collect(); + let copy_keys: Vec<_> = copy_inner.buckets.keys().clone().collect(); + + assert_eq!(routing_table_keys.len(), copy_keys.len()); + + for crypto in routing_table_keys { + // The same keys are present in the original and copy RoutingTables. + let original_buckets = original_inner.buckets.get(&crypto).unwrap(); + let copy_buckets = copy_inner.buckets.get(&crypto).unwrap(); + + // Recurse into RoutingTable.inner.buckets + for (left_buckets, right_buckets) in original_buckets.iter().zip(copy_buckets.iter()) { + // Recurse into RoutingTable.inner.buckets.entries + for ((left_crypto, left_entries), (right_crypto, right_entries)) in + left_buckets.entries().zip(right_buckets.entries()) + { + assert_eq!(left_crypto, right_crypto); + + assert_eq!( + format!("{:?}", left_entries), + format!("{:?}", right_entries) + ); + } + } + } +} + +pub async fn test_all() { + test_routingtable_buckets_round_trip().await; +} diff --git a/veilid-core/src/tests/mod.rs b/veilid-core/src/tests/mod.rs index 609182dd..8e9d815f 100644 --- a/veilid-core/src/tests/mod.rs +++ b/veilid-core/src/tests/mod.rs @@ -12,4 +12,5 @@ use super::*; pub use common::*; pub use crypto::tests::*; pub use network_manager::tests::*; +pub use routing_table::tests::*; pub use veilid_api::tests::*; diff --git a/veilid-core/src/tests/native/mod.rs b/veilid-core/src/tests/native/mod.rs index f7fcef81..57855174 100644 --- a/veilid-core/src/tests/native/mod.rs +++ b/veilid-core/src/tests/native/mod.rs @@ -2,6 +2,7 @@ #![cfg(not(target_arch = "wasm32"))] use crate::crypto::tests::*; use crate::network_manager::tests::*; +use crate::routing_table::tests::*; use crate::tests::common::*; use crate::veilid_api::tests::*; use crate::*; @@ -173,5 +174,13 @@ cfg_if! { }) } + #[test] + #[serial] + fn run_test_routing_table_serialize() { + setup(); + block_on(async { + routing_table::tests::test_serialize::test_all().await; + }) + } } }