Re-enable randomized concurrent edits test

Max Brunsfeld created

Change summary

crates/buffer/src/lib.rs                  |  46 ++---
crates/buffer/src/tests/buffer.rs         | 172 ++++++++++++------------
crates/editor/src/display_map.rs          |   2 
crates/editor/src/display_map/fold_map.rs |   4 
crates/editor/src/display_map/wrap_map.rs |   2 
5 files changed, 108 insertions(+), 118 deletions(-)

Detailed changes

crates/buffer/src/lib.rs 🔗

@@ -2414,27 +2414,18 @@ impl Buffer {
 
 #[cfg(any(test, feature = "test-support"))]
 impl Buffer {
-    pub fn randomly_edit<T>(
-        &mut self,
-        rng: &mut T,
-        old_range_count: usize,
-        _: &mut ModelContext<Self>,
-    ) -> (Vec<Range<usize>>, String)
+    pub fn randomly_edit<T>(&mut self, rng: &mut T, old_range_count: usize)
     where
         T: rand::Rng,
     {
-        self.buffer.randomly_edit(rng, old_range_count)
+        self.buffer.randomly_edit(rng, old_range_count);
     }
 
-    pub fn randomly_mutate<T>(
-        &mut self,
-        rng: &mut T,
-        _: &mut ModelContext<Self>,
-    ) -> (Vec<Range<usize>>, String)
+    pub fn randomly_mutate<T>(&mut self, rng: &mut T)
     where
         T: rand::Rng,
     {
-        self.buffer.randomly_mutate(rng)
+        self.buffer.randomly_mutate(rng);
     }
 }
 
@@ -2450,7 +2441,7 @@ impl TextBuffer {
         &mut self,
         rng: &mut T,
         old_range_count: usize,
-    ) -> (Vec<Range<usize>>, String)
+    ) -> (Vec<Range<usize>>, String, Operation)
     where
         T: rand::Rng,
     {
@@ -2472,17 +2463,17 @@ impl TextBuffer {
             old_ranges,
             new_text
         );
-        self.edit(old_ranges.iter().cloned(), new_text.as_str());
-        (old_ranges, new_text)
+        let op = self.edit(old_ranges.iter().cloned(), new_text.as_str());
+        (old_ranges, new_text, Operation::Edit(op))
     }
 
-    pub fn randomly_mutate<T>(&mut self, rng: &mut T) -> (Vec<Range<usize>>, String)
+    pub fn randomly_mutate<T>(&mut self, rng: &mut T) -> Vec<Operation>
     where
         T: rand::Rng,
     {
         use rand::prelude::*;
 
-        let (old_ranges, new_text) = self.randomly_edit(rng, 5);
+        let mut ops = vec![self.randomly_edit(rng, 5).2];
 
         // Randomly add, remove or mutate selection sets.
         let replica_selection_sets = &self
@@ -2492,7 +2483,7 @@ impl TextBuffer {
             .collect::<Vec<_>>();
         let set_id = replica_selection_sets.choose(rng);
         if set_id.is_some() && rng.gen_bool(1.0 / 6.0) {
-            self.remove_selection_set(*set_id.unwrap()).unwrap();
+            ops.push(self.remove_selection_set(*set_id.unwrap()).unwrap());
         } else {
             let mut ranges = Vec::new();
             for _ in 0..5 {
@@ -2500,20 +2491,22 @@ impl TextBuffer {
             }
             let new_selections = self.selections_from_ranges(ranges).unwrap();
 
-            if set_id.is_none() || rng.gen_bool(1.0 / 5.0) {
-                self.add_selection_set(new_selections);
+            let op = if set_id.is_none() || rng.gen_bool(1.0 / 5.0) {
+                self.add_selection_set(new_selections)
             } else {
                 self.update_selection_set(*set_id.unwrap(), new_selections)
-                    .unwrap();
-            }
+                    .unwrap()
+            };
+            ops.push(op);
         }
 
-        (old_ranges, new_text)
+        ops
     }
 
-    pub fn randomly_undo_redo(&mut self, rng: &mut impl rand::Rng) {
+    pub fn randomly_undo_redo(&mut self, rng: &mut impl rand::Rng) -> Vec<Operation> {
         use rand::prelude::*;
 
+        let mut ops = Vec::new();
         for _ in 0..rng.gen_range(1..=5) {
             if let Some(transaction) = self.history.undo_stack.choose(rng).cloned() {
                 log::info!(
@@ -2521,9 +2514,10 @@ impl TextBuffer {
                     self.replica_id,
                     transaction
                 );
-                self.undo_or_redo(transaction).unwrap();
+                ops.push(self.undo_or_redo(transaction).unwrap());
             }
         }
+        ops
     }
 
     fn selections_from_ranges<I>(&self, ranges: I) -> Result<Vec<Selection>>

crates/buffer/src/tests/buffer.rs 🔗

@@ -6,7 +6,6 @@ use std::{
     cmp::Ordering,
     env,
     iter::Iterator,
-    mem,
     rc::Rc,
     time::{Duration, Instant},
 };
@@ -104,7 +103,7 @@ fn test_random_edits(mut rng: StdRng) {
     );
 
     for _i in 0..operations {
-        let (old_ranges, new_text) = buffer.randomly_mutate(&mut rng);
+        let (old_ranges, new_text, _) = buffer.randomly_edit(&mut rng, 5);
         for old_range in old_ranges.iter().rev() {
             reference_string.replace_range(old_range.clone(), &new_text);
         }
@@ -571,92 +570,89 @@ fn test_concurrent_edits() {
     assert_eq!(buffer3.text(), "a12c34e56");
 }
 
-// #[gpui::test(iterations = 100)]
-// fn test_random_concurrent_edits(cx: &mut gpui::MutableAppContext, mut rng: StdRng) {
-//     let peers = env::var("PEERS")
-//         .map(|i| i.parse().expect("invalid `PEERS` variable"))
-//         .unwrap_or(5);
-//     let operations = env::var("OPERATIONS")
-//         .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
-//         .unwrap_or(10);
-
-//     let base_text_len = rng.gen_range(0..10);
-//     let base_text = RandomCharIter::new(&mut rng)
-//         .take(base_text_len)
-//         .collect::<String>();
-//     let mut replica_ids = Vec::new();
-//     let mut buffers = Vec::new();
-//     let mut network = Network::new(rng.clone());
-
-//     for i in 0..peers {
-//         let buffer = cx.add_model(|cx| {
-//             let mut buf = Buffer::new(i as ReplicaId, base_text.as_str(), cx);
-//             buf.history.group_interval = Duration::from_millis(rng.gen_range(0..=200));
-//             buf
-//         });
-//         buffers.push(buffer);
-//         replica_ids.push(i as u16);
-//         network.add_peer(i as u16);
-//     }
-
-//     log::info!("initial text: {:?}", base_text);
-
-//     let mut mutation_count = operations;
-//     loop {
-//         let replica_index = rng.gen_range(0..peers);
-//         let replica_id = replica_ids[replica_index];
-//         buffers[replica_index].update(cx, |buffer, cx| match rng.gen_range(0..=100) {
-//             0..=50 if mutation_count != 0 => {
-//                 buffer.randomly_mutate(&mut rng, cx);
-//                 network.broadcast(buffer.replica_id, mem::take(&mut buffer.operations));
-//                 log::info!("buffer {} text: {:?}", buffer.replica_id, buffer.text());
-//                 mutation_count -= 1;
-//             }
-//             51..=70 if mutation_count != 0 => {
-//                 buffer.randomly_undo_redo(&mut rng, cx);
-//                 network.broadcast(buffer.replica_id, mem::take(&mut buffer.operations));
-//                 mutation_count -= 1;
-//             }
-//             71..=100 if network.has_unreceived(replica_id) => {
-//                 let ops = network.receive(replica_id);
-//                 if !ops.is_empty() {
-//                     log::info!(
-//                         "peer {} applying {} ops from the network.",
-//                         replica_id,
-//                         ops.len()
-//                     );
-//                     buffer.apply_ops(ops, cx).unwrap();
-//                 }
-//             }
-//             _ => {}
-//         });
-
-//         if mutation_count == 0 && network.is_idle() {
-//             break;
-//         }
-//     }
-
-//     let first_buffer = buffers[0].read(cx);
-//     for buffer in &buffers[1..] {
-//         let buffer = buffer.read(cx);
-//         assert_eq!(
-//             buffer.text(),
-//             first_buffer.text(),
-//             "Replica {} text != Replica 0 text",
-//             buffer.replica_id
-//         );
-//         assert_eq!(
-//             buffer.selection_sets().collect::<HashMap<_, _>>(),
-//             first_buffer.selection_sets().collect::<HashMap<_, _>>()
-//         );
-//         assert_eq!(
-//             buffer.all_selection_ranges().collect::<HashMap<_, _>>(),
-//             first_buffer
-//                 .all_selection_ranges()
-//                 .collect::<HashMap<_, _>>()
-//         );
-//     }
-// }
+#[gpui::test(iterations = 100)]
+fn test_random_concurrent_edits(mut rng: StdRng) {
+    let peers = env::var("PEERS")
+        .map(|i| i.parse().expect("invalid `PEERS` variable"))
+        .unwrap_or(5);
+    let operations = env::var("OPERATIONS")
+        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
+        .unwrap_or(10);
+
+    let base_text_len = rng.gen_range(0..10);
+    let base_text = RandomCharIter::new(&mut rng)
+        .take(base_text_len)
+        .collect::<String>();
+    let mut replica_ids = Vec::new();
+    let mut buffers = Vec::new();
+    let mut network = Network::new(rng.clone());
+
+    for i in 0..peers {
+        let mut buffer = TextBuffer::new(i as ReplicaId, 0, History::new(base_text.clone().into()));
+        buffer.history.group_interval = Duration::from_millis(rng.gen_range(0..=200));
+        buffers.push(buffer);
+        replica_ids.push(i as u16);
+        network.add_peer(i as u16);
+    }
+
+    log::info!("initial text: {:?}", base_text);
+
+    let mut mutation_count = operations;
+    loop {
+        let replica_index = rng.gen_range(0..peers);
+        let replica_id = replica_ids[replica_index];
+        let buffer = &mut buffers[replica_index];
+        match rng.gen_range(0..=100) {
+            0..=50 if mutation_count != 0 => {
+                let ops = buffer.randomly_mutate(&mut rng);
+                network.broadcast(buffer.replica_id, ops);
+                log::info!("buffer {} text: {:?}", buffer.replica_id, buffer.text());
+                mutation_count -= 1;
+            }
+            51..=70 if mutation_count != 0 => {
+                let ops = buffer.randomly_undo_redo(&mut rng);
+                network.broadcast(buffer.replica_id, ops);
+                mutation_count -= 1;
+            }
+            71..=100 if network.has_unreceived(replica_id) => {
+                let ops = network.receive(replica_id);
+                if !ops.is_empty() {
+                    log::info!(
+                        "peer {} applying {} ops from the network.",
+                        replica_id,
+                        ops.len()
+                    );
+                    buffer.apply_ops(ops).unwrap();
+                }
+            }
+            _ => {}
+        }
+
+        if mutation_count == 0 && network.is_idle() {
+            break;
+        }
+    }
+
+    let first_buffer = &buffers[0];
+    for buffer in &buffers[1..] {
+        assert_eq!(
+            buffer.text(),
+            first_buffer.text(),
+            "Replica {} text != Replica 0 text",
+            buffer.replica_id
+        );
+        assert_eq!(
+            buffer.selection_sets().collect::<HashMap<_, _>>(),
+            first_buffer.selection_sets().collect::<HashMap<_, _>>()
+        );
+        assert_eq!(
+            buffer.all_selection_ranges().collect::<HashMap<_, _>>(),
+            first_buffer
+                .all_selection_ranges()
+                .collect::<HashMap<_, _>>()
+        );
+    }
+}
 
 #[derive(Clone)]
 struct Envelope<T: Clone> {

crates/editor/src/display_map.rs 🔗

@@ -436,7 +436,7 @@ mod tests {
                     }
                 }
                 _ => {
-                    buffer.update(&mut cx, |buffer, cx| buffer.randomly_edit(&mut rng, 5, cx));
+                    buffer.update(&mut cx, |buffer, cx| buffer.randomly_edit(&mut rng, 5));
                 }
             }
 

crates/editor/src/display_map/fold_map.rs 🔗

@@ -1,4 +1,4 @@
-use buffer::{Anchor, Buffer, Point, ToOffset, AnchorRangeExt, HighlightId, TextSummary};
+use buffer::{Anchor, AnchorRangeExt, Buffer, HighlightId, Point, TextSummary, ToOffset};
 use gpui::{AppContext, ModelHandle};
 use parking_lot::Mutex;
 use std::{
@@ -1334,7 +1334,7 @@ mod tests {
                     let edits = buffer.update(cx, |buffer, cx| {
                         let start_version = buffer.version.clone();
                         let edit_count = rng.gen_range(1..=5);
-                        buffer.randomly_edit(&mut rng, edit_count, cx);
+                        buffer.randomly_edit(&mut rng, edit_count);
                         buffer.edits_since(start_version).collect::<Vec<_>>()
                     });
                     log::info!("editing {:?}", edits);

crates/editor/src/display_map/wrap_map.rs 🔗

@@ -990,7 +990,7 @@ mod tests {
                     }
                 }
                 _ => {
-                    buffer.update(&mut cx, |buffer, cx| buffer.randomly_mutate(&mut rng, cx));
+                    buffer.update(&mut cx, |buffer, cx| buffer.randomly_mutate(&mut rng));
                 }
             }