Introduce randomized test for collaboration on buffers

This test will exercise serialization of operations as well as peers
replicating from an existing buffer.
This commit is contained in:
Antonio Scandurra 2022-01-05 11:51:41 +01:00
parent 7bc8eb4f3d
commit d383ff30ce
8 changed files with 264 additions and 85 deletions

View file

@ -4,12 +4,16 @@ version = "0.1.0"
edition = "2018"
[features]
test-support = ["serde_json", "tempdir"]
test-support = ["clock", "rand", "serde_json", "tempdir"]
[dependencies]
clock = { path = "../clock", optional = true }
anyhow = "1.0.38"
futures = "0.3"
log = "0.4"
rand = { version = "0.8", optional = true }
surf = "2.2"
tempdir = { version = "0.3.7", optional = true }
serde_json = { version = "1.0.64", features = ["preserve_order"], optional = true }
serde_json = { version = "1.0.64", features = [
"preserve_order"
], optional = true }

View file

@ -1,6 +1,90 @@
use clock::ReplicaId;
use std::path::{Path, PathBuf};
use tempdir::TempDir;
#[derive(Clone)]
struct Envelope<T: Clone> {
message: T,
sender: ReplicaId,
}
pub struct Network<T: Clone, R: rand::Rng> {
inboxes: std::collections::BTreeMap<ReplicaId, Vec<Envelope<T>>>,
all_messages: Vec<T>,
rng: R,
}
impl<T: Clone, R: rand::Rng> Network<T, R> {
pub fn new(rng: R) -> Self {
Network {
inboxes: Default::default(),
all_messages: Vec::new(),
rng,
}
}
pub fn add_peer(&mut self, id: ReplicaId) {
self.inboxes.insert(id, Vec::new());
}
pub fn replicate(&mut self, old_replica_id: ReplicaId, new_replica_id: ReplicaId) {
self.inboxes
.insert(new_replica_id, self.inboxes[&old_replica_id].clone());
}
pub fn is_idle(&self) -> bool {
self.inboxes.values().all(|i| i.is_empty())
}
pub fn broadcast(&mut self, sender: ReplicaId, messages: Vec<T>) {
for (replica, inbox) in self.inboxes.iter_mut() {
if *replica != sender {
for message in &messages {
let min_index = inbox
.iter()
.enumerate()
.rev()
.find_map(|(index, envelope)| {
if sender == envelope.sender {
Some(index + 1)
} else {
None
}
})
.unwrap_or(0);
// Insert one or more duplicates of this message *after* the previous
// message delivered by this replica.
for _ in 0..self.rng.gen_range(1..4) {
let insertion_index = self.rng.gen_range(min_index..inbox.len() + 1);
inbox.insert(
insertion_index,
Envelope {
message: message.clone(),
sender,
},
);
}
}
}
}
self.all_messages.extend(messages);
}
pub fn has_unreceived(&self, receiver: ReplicaId) -> bool {
!self.inboxes[&receiver].is_empty()
}
pub fn receive(&mut self, receiver: ReplicaId) -> Vec<T> {
let inbox = self.inboxes.get_mut(&receiver).unwrap();
let count = self.rng.gen_range(0..inbox.len() + 1);
inbox
.drain(0..count)
.map(|envelope| envelope.message)
.collect()
}
}
pub fn temp_tree(tree: serde_json::Value) -> TempDir {
let dir = TempDir::new("").unwrap();
write_tree(dir.path(), tree);