test.rs

  1use clock::ReplicaId;
  2use std::path::{Path, PathBuf};
  3use tempdir::TempDir;
  4
  5#[derive(Clone)]
  6struct Envelope<T: Clone> {
  7    message: T,
  8    sender: ReplicaId,
  9}
 10
 11pub struct Network<T: Clone, R: rand::Rng> {
 12    inboxes: std::collections::BTreeMap<ReplicaId, Vec<Envelope<T>>>,
 13    all_messages: Vec<T>,
 14    rng: R,
 15}
 16
 17impl<T: Clone, R: rand::Rng> Network<T, R> {
 18    pub fn new(rng: R) -> Self {
 19        Network {
 20            inboxes: Default::default(),
 21            all_messages: Vec::new(),
 22            rng,
 23        }
 24    }
 25
 26    pub fn add_peer(&mut self, id: ReplicaId) {
 27        self.inboxes.insert(id, Vec::new());
 28    }
 29
 30    pub fn replicate(&mut self, old_replica_id: ReplicaId, new_replica_id: ReplicaId) {
 31        self.inboxes
 32            .insert(new_replica_id, self.inboxes[&old_replica_id].clone());
 33    }
 34
 35    pub fn is_idle(&self) -> bool {
 36        self.inboxes.values().all(|i| i.is_empty())
 37    }
 38
 39    pub fn broadcast(&mut self, sender: ReplicaId, messages: Vec<T>) {
 40        for (replica, inbox) in self.inboxes.iter_mut() {
 41            if *replica != sender {
 42                for message in &messages {
 43                    let min_index = inbox
 44                        .iter()
 45                        .enumerate()
 46                        .rev()
 47                        .find_map(|(index, envelope)| {
 48                            if sender == envelope.sender {
 49                                Some(index + 1)
 50                            } else {
 51                                None
 52                            }
 53                        })
 54                        .unwrap_or(0);
 55
 56                    // Insert one or more duplicates of this message *after* the previous
 57                    // message delivered by this replica.
 58                    for _ in 0..self.rng.gen_range(1..4) {
 59                        let insertion_index = self.rng.gen_range(min_index..inbox.len() + 1);
 60                        inbox.insert(
 61                            insertion_index,
 62                            Envelope {
 63                                message: message.clone(),
 64                                sender,
 65                            },
 66                        );
 67                    }
 68                }
 69            }
 70        }
 71        self.all_messages.extend(messages);
 72    }
 73
 74    pub fn has_unreceived(&self, receiver: ReplicaId) -> bool {
 75        !self.inboxes[&receiver].is_empty()
 76    }
 77
 78    pub fn receive(&mut self, receiver: ReplicaId) -> Vec<T> {
 79        let inbox = self.inboxes.get_mut(&receiver).unwrap();
 80        let count = self.rng.gen_range(0..inbox.len() + 1);
 81        inbox
 82            .drain(0..count)
 83            .map(|envelope| envelope.message)
 84            .collect()
 85    }
 86}
 87
 88pub fn temp_tree(tree: serde_json::Value) -> TempDir {
 89    let dir = TempDir::new("").unwrap();
 90    write_tree(dir.path(), tree);
 91    dir
 92}
 93
 94fn write_tree(path: &Path, tree: serde_json::Value) {
 95    use serde_json::Value;
 96    use std::fs;
 97
 98    if let Value::Object(map) = tree {
 99        for (name, contents) in map {
100            let mut path = PathBuf::from(path);
101            path.push(name);
102            match contents {
103                Value::Object(_) => {
104                    fs::create_dir(&path).unwrap();
105                    write_tree(&path, contents);
106                }
107                Value::Null => {
108                    fs::create_dir(&path).unwrap();
109                }
110                Value::String(contents) => {
111                    fs::write(&path, contents).unwrap();
112                }
113                _ => {
114                    panic!("JSON object must contain only objects, strings, or null");
115                }
116            }
117        }
118    } else {
119        panic!("You must pass a JSON object to this helper")
120    }
121}
122
123pub fn sample_text(rows: usize, cols: usize, start_char: char) -> String {
124    let mut text = String::new();
125    for row in 0..rows {
126        let c: char = (start_char as u32 + row as u32) as u8 as char;
127        let mut line = c.to_string().repeat(cols);
128        if row < rows - 1 {
129            line.push('\n');
130        }
131        text += &line;
132    }
133    text
134}