test.rs

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