buffer.rs

  1use crate::*;
  2use clock::ReplicaId;
  3use rand::prelude::*;
  4use std::{
  5    cell::RefCell,
  6    cmp::Ordering,
  7    env,
  8    iter::Iterator,
  9    mem,
 10    rc::Rc,
 11    time::{Duration, Instant},
 12};
 13
 14#[gpui::test]
 15fn test_edit(cx: &mut gpui::MutableAppContext) {
 16    cx.add_model(|cx| {
 17        let mut buffer = Buffer::new(0, "abc", cx);
 18        assert_eq!(buffer.text(), "abc");
 19        buffer.edit(vec![3..3], "def", cx);
 20        assert_eq!(buffer.text(), "abcdef");
 21        buffer.edit(vec![0..0], "ghi", cx);
 22        assert_eq!(buffer.text(), "ghiabcdef");
 23        buffer.edit(vec![5..5], "jkl", cx);
 24        assert_eq!(buffer.text(), "ghiabjklcdef");
 25        buffer.edit(vec![6..7], "", cx);
 26        assert_eq!(buffer.text(), "ghiabjlcdef");
 27        buffer.edit(vec![4..9], "mno", cx);
 28        assert_eq!(buffer.text(), "ghiamnoef");
 29        buffer
 30    });
 31}
 32
 33#[gpui::test]
 34fn test_edit_events(cx: &mut gpui::MutableAppContext) {
 35    let mut now = Instant::now();
 36    let buffer_1_events = Rc::new(RefCell::new(Vec::new()));
 37    let buffer_2_events = Rc::new(RefCell::new(Vec::new()));
 38
 39    let buffer1 = cx.add_model(|cx| Buffer::new(0, "abcdef", cx));
 40    let buffer2 = cx.add_model(|cx| Buffer::new(1, "abcdef", cx));
 41    let buffer_ops = buffer1.update(cx, |buffer, cx| {
 42        let buffer_1_events = buffer_1_events.clone();
 43        cx.subscribe(&buffer1, move |_, _, event, _| {
 44            buffer_1_events.borrow_mut().push(event.clone())
 45        })
 46        .detach();
 47        let buffer_2_events = buffer_2_events.clone();
 48        cx.subscribe(&buffer2, move |_, _, event, _| {
 49            buffer_2_events.borrow_mut().push(event.clone())
 50        })
 51        .detach();
 52
 53        // An edit emits an edited event, followed by a dirtied event,
 54        // since the buffer was previously in a clean state.
 55        buffer.edit(Some(2..4), "XYZ", cx);
 56
 57        // An empty transaction does not emit any events.
 58        buffer.start_transaction(None).unwrap();
 59        buffer.end_transaction(None, cx).unwrap();
 60
 61        // A transaction containing two edits emits one edited event.
 62        now += Duration::from_secs(1);
 63        buffer.start_transaction_at(None, now).unwrap();
 64        buffer.edit(Some(5..5), "u", cx);
 65        buffer.edit(Some(6..6), "w", cx);
 66        buffer.end_transaction_at(None, now, cx).unwrap();
 67
 68        // Undoing a transaction emits one edited event.
 69        buffer.undo(cx);
 70
 71        buffer.operations.clone()
 72    });
 73
 74    // Incorporating a set of remote ops emits a single edited event,
 75    // followed by a dirtied event.
 76    buffer2.update(cx, |buffer, cx| {
 77        buffer.apply_ops(buffer_ops, cx).unwrap();
 78    });
 79
 80    let buffer_1_events = buffer_1_events.borrow();
 81    assert_eq!(
 82        *buffer_1_events,
 83        vec![Event::Edited, Event::Dirtied, Event::Edited, Event::Edited]
 84    );
 85
 86    let buffer_2_events = buffer_2_events.borrow();
 87    assert_eq!(*buffer_2_events, vec![Event::Edited, Event::Dirtied]);
 88}
 89
 90#[gpui::test(iterations = 100)]
 91fn test_random_edits(cx: &mut gpui::MutableAppContext, mut rng: StdRng) {
 92    let operations = env::var("OPERATIONS")
 93        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
 94        .unwrap_or(10);
 95
 96    let reference_string_len = rng.gen_range(0..3);
 97    let mut reference_string = RandomCharIter::new(&mut rng)
 98        .take(reference_string_len)
 99        .collect::<String>();
100    cx.add_model(|cx| {
101        let mut buffer = Buffer::new(0, reference_string.as_str(), cx);
102        buffer.history.group_interval = Duration::from_millis(rng.gen_range(0..=200));
103        let mut buffer_versions = Vec::new();
104        log::info!(
105            "buffer text {:?}, version: {:?}",
106            buffer.text(),
107            buffer.version()
108        );
109
110        for _i in 0..operations {
111            let (old_ranges, new_text) = buffer.randomly_mutate(&mut rng, cx);
112            for old_range in old_ranges.iter().rev() {
113                reference_string.replace_range(old_range.clone(), &new_text);
114            }
115            assert_eq!(buffer.text(), reference_string);
116            log::info!(
117                "buffer text {:?}, version: {:?}",
118                buffer.text(),
119                buffer.version()
120            );
121
122            if rng.gen_bool(0.25) {
123                buffer.randomly_undo_redo(&mut rng, cx);
124                reference_string = buffer.text();
125                log::info!(
126                    "buffer text {:?}, version: {:?}",
127                    buffer.text(),
128                    buffer.version()
129                );
130            }
131
132            let range = buffer.random_byte_range(0, &mut rng);
133            assert_eq!(
134                buffer.text_summary_for_range(range.clone()),
135                TextSummary::from(&reference_string[range])
136            );
137
138            if rng.gen_bool(0.3) {
139                buffer_versions.push(buffer.clone());
140            }
141        }
142
143        for mut old_buffer in buffer_versions {
144            let edits = buffer
145                .edits_since(old_buffer.version.clone())
146                .collect::<Vec<_>>();
147
148            log::info!(
149                "mutating old buffer version {:?}, text: {:?}, edits since: {:?}",
150                old_buffer.version(),
151                old_buffer.text(),
152                edits,
153            );
154
155            let mut delta = 0_isize;
156            for edit in edits {
157                let old_start = (edit.old_bytes.start as isize + delta) as usize;
158                let new_text: String = buffer.text_for_range(edit.new_bytes.clone()).collect();
159                old_buffer.edit(
160                    Some(old_start..old_start + edit.deleted_bytes()),
161                    new_text,
162                    cx,
163                );
164                delta += edit.delta();
165            }
166            assert_eq!(old_buffer.text(), buffer.text());
167        }
168
169        buffer
170    });
171}
172
173#[gpui::test]
174fn test_line_len(cx: &mut gpui::MutableAppContext) {
175    cx.add_model(|cx| {
176        let mut buffer = Buffer::new(0, "", cx);
177        buffer.edit(vec![0..0], "abcd\nefg\nhij", cx);
178        buffer.edit(vec![12..12], "kl\nmno", cx);
179        buffer.edit(vec![18..18], "\npqrs\n", cx);
180        buffer.edit(vec![18..21], "\nPQ", cx);
181
182        assert_eq!(buffer.line_len(0), 4);
183        assert_eq!(buffer.line_len(1), 3);
184        assert_eq!(buffer.line_len(2), 5);
185        assert_eq!(buffer.line_len(3), 3);
186        assert_eq!(buffer.line_len(4), 4);
187        assert_eq!(buffer.line_len(5), 0);
188        buffer
189    });
190}
191
192#[gpui::test]
193fn test_text_summary_for_range(cx: &mut gpui::MutableAppContext) {
194    cx.add_model(|cx| {
195        let buffer = Buffer::new(0, "ab\nefg\nhklm\nnopqrs\ntuvwxyz", cx);
196        assert_eq!(
197            buffer.text_summary_for_range(1..3),
198            TextSummary {
199                bytes: 2,
200                lines: Point::new(1, 0),
201                first_line_chars: 1,
202                last_line_chars: 0,
203                longest_row: 0,
204                longest_row_chars: 1,
205            }
206        );
207        assert_eq!(
208            buffer.text_summary_for_range(1..12),
209            TextSummary {
210                bytes: 11,
211                lines: Point::new(3, 0),
212                first_line_chars: 1,
213                last_line_chars: 0,
214                longest_row: 2,
215                longest_row_chars: 4,
216            }
217        );
218        assert_eq!(
219            buffer.text_summary_for_range(0..20),
220            TextSummary {
221                bytes: 20,
222                lines: Point::new(4, 1),
223                first_line_chars: 2,
224                last_line_chars: 1,
225                longest_row: 3,
226                longest_row_chars: 6,
227            }
228        );
229        assert_eq!(
230            buffer.text_summary_for_range(0..22),
231            TextSummary {
232                bytes: 22,
233                lines: Point::new(4, 3),
234                first_line_chars: 2,
235                last_line_chars: 3,
236                longest_row: 3,
237                longest_row_chars: 6,
238            }
239        );
240        assert_eq!(
241            buffer.text_summary_for_range(7..22),
242            TextSummary {
243                bytes: 15,
244                lines: Point::new(2, 3),
245                first_line_chars: 4,
246                last_line_chars: 3,
247                longest_row: 1,
248                longest_row_chars: 6,
249            }
250        );
251        buffer
252    });
253}
254
255#[gpui::test]
256fn test_chars_at(cx: &mut gpui::MutableAppContext) {
257    cx.add_model(|cx| {
258        let mut buffer = Buffer::new(0, "", cx);
259        buffer.edit(vec![0..0], "abcd\nefgh\nij", cx);
260        buffer.edit(vec![12..12], "kl\nmno", cx);
261        buffer.edit(vec![18..18], "\npqrs", cx);
262        buffer.edit(vec![18..21], "\nPQ", cx);
263
264        let chars = buffer.chars_at(Point::new(0, 0));
265        assert_eq!(chars.collect::<String>(), "abcd\nefgh\nijkl\nmno\nPQrs");
266
267        let chars = buffer.chars_at(Point::new(1, 0));
268        assert_eq!(chars.collect::<String>(), "efgh\nijkl\nmno\nPQrs");
269
270        let chars = buffer.chars_at(Point::new(2, 0));
271        assert_eq!(chars.collect::<String>(), "ijkl\nmno\nPQrs");
272
273        let chars = buffer.chars_at(Point::new(3, 0));
274        assert_eq!(chars.collect::<String>(), "mno\nPQrs");
275
276        let chars = buffer.chars_at(Point::new(4, 0));
277        assert_eq!(chars.collect::<String>(), "PQrs");
278
279        // Regression test:
280        let mut buffer = Buffer::new(0, "", cx);
281        buffer.edit(vec![0..0], "[workspace]\nmembers = [\n    \"xray_core\",\n    \"xray_server\",\n    \"xray_cli\",\n    \"xray_wasm\",\n]\n", cx);
282        buffer.edit(vec![60..60], "\n", cx);
283
284        let chars = buffer.chars_at(Point::new(6, 0));
285        assert_eq!(chars.collect::<String>(), "    \"xray_wasm\",\n]\n");
286
287        buffer
288    });
289}
290
291#[gpui::test]
292fn test_anchors(cx: &mut gpui::MutableAppContext) {
293    cx.add_model(|cx| {
294        let mut buffer = Buffer::new(0, "", cx);
295        buffer.edit(vec![0..0], "abc", cx);
296        let left_anchor = buffer.anchor_before(2);
297        let right_anchor = buffer.anchor_after(2);
298
299        buffer.edit(vec![1..1], "def\n", cx);
300        assert_eq!(buffer.text(), "adef\nbc");
301        assert_eq!(left_anchor.to_offset(&buffer), 6);
302        assert_eq!(right_anchor.to_offset(&buffer), 6);
303        assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
304        assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
305
306        buffer.edit(vec![2..3], "", cx);
307        assert_eq!(buffer.text(), "adf\nbc");
308        assert_eq!(left_anchor.to_offset(&buffer), 5);
309        assert_eq!(right_anchor.to_offset(&buffer), 5);
310        assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
311        assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
312
313        buffer.edit(vec![5..5], "ghi\n", cx);
314        assert_eq!(buffer.text(), "adf\nbghi\nc");
315        assert_eq!(left_anchor.to_offset(&buffer), 5);
316        assert_eq!(right_anchor.to_offset(&buffer), 9);
317        assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
318        assert_eq!(right_anchor.to_point(&buffer), Point { row: 2, column: 0 });
319
320        buffer.edit(vec![7..9], "", cx);
321        assert_eq!(buffer.text(), "adf\nbghc");
322        assert_eq!(left_anchor.to_offset(&buffer), 5);
323        assert_eq!(right_anchor.to_offset(&buffer), 7);
324        assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 },);
325        assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 3 });
326
327        // Ensure anchoring to a point is equivalent to anchoring to an offset.
328        assert_eq!(
329            buffer.anchor_before(Point { row: 0, column: 0 }),
330            buffer.anchor_before(0)
331        );
332        assert_eq!(
333            buffer.anchor_before(Point { row: 0, column: 1 }),
334            buffer.anchor_before(1)
335        );
336        assert_eq!(
337            buffer.anchor_before(Point { row: 0, column: 2 }),
338            buffer.anchor_before(2)
339        );
340        assert_eq!(
341            buffer.anchor_before(Point { row: 0, column: 3 }),
342            buffer.anchor_before(3)
343        );
344        assert_eq!(
345            buffer.anchor_before(Point { row: 1, column: 0 }),
346            buffer.anchor_before(4)
347        );
348        assert_eq!(
349            buffer.anchor_before(Point { row: 1, column: 1 }),
350            buffer.anchor_before(5)
351        );
352        assert_eq!(
353            buffer.anchor_before(Point { row: 1, column: 2 }),
354            buffer.anchor_before(6)
355        );
356        assert_eq!(
357            buffer.anchor_before(Point { row: 1, column: 3 }),
358            buffer.anchor_before(7)
359        );
360        assert_eq!(
361            buffer.anchor_before(Point { row: 1, column: 4 }),
362            buffer.anchor_before(8)
363        );
364
365        // Comparison between anchors.
366        let anchor_at_offset_0 = buffer.anchor_before(0);
367        let anchor_at_offset_1 = buffer.anchor_before(1);
368        let anchor_at_offset_2 = buffer.anchor_before(2);
369
370        assert_eq!(
371            anchor_at_offset_0
372                .cmp(&anchor_at_offset_0, &buffer)
373                .unwrap(),
374            Ordering::Equal
375        );
376        assert_eq!(
377            anchor_at_offset_1
378                .cmp(&anchor_at_offset_1, &buffer)
379                .unwrap(),
380            Ordering::Equal
381        );
382        assert_eq!(
383            anchor_at_offset_2
384                .cmp(&anchor_at_offset_2, &buffer)
385                .unwrap(),
386            Ordering::Equal
387        );
388
389        assert_eq!(
390            anchor_at_offset_0
391                .cmp(&anchor_at_offset_1, &buffer)
392                .unwrap(),
393            Ordering::Less
394        );
395        assert_eq!(
396            anchor_at_offset_1
397                .cmp(&anchor_at_offset_2, &buffer)
398                .unwrap(),
399            Ordering::Less
400        );
401        assert_eq!(
402            anchor_at_offset_0
403                .cmp(&anchor_at_offset_2, &buffer)
404                .unwrap(),
405            Ordering::Less
406        );
407
408        assert_eq!(
409            anchor_at_offset_1
410                .cmp(&anchor_at_offset_0, &buffer)
411                .unwrap(),
412            Ordering::Greater
413        );
414        assert_eq!(
415            anchor_at_offset_2
416                .cmp(&anchor_at_offset_1, &buffer)
417                .unwrap(),
418            Ordering::Greater
419        );
420        assert_eq!(
421            anchor_at_offset_2
422                .cmp(&anchor_at_offset_0, &buffer)
423                .unwrap(),
424            Ordering::Greater
425        );
426        buffer
427    });
428}
429
430#[gpui::test]
431fn test_anchors_at_start_and_end(cx: &mut gpui::MutableAppContext) {
432    cx.add_model(|cx| {
433        let mut buffer = Buffer::new(0, "", cx);
434        let before_start_anchor = buffer.anchor_before(0);
435        let after_end_anchor = buffer.anchor_after(0);
436
437        buffer.edit(vec![0..0], "abc", cx);
438        assert_eq!(buffer.text(), "abc");
439        assert_eq!(before_start_anchor.to_offset(&buffer), 0);
440        assert_eq!(after_end_anchor.to_offset(&buffer), 3);
441
442        let after_start_anchor = buffer.anchor_after(0);
443        let before_end_anchor = buffer.anchor_before(3);
444
445        buffer.edit(vec![3..3], "def", cx);
446        buffer.edit(vec![0..0], "ghi", cx);
447        assert_eq!(buffer.text(), "ghiabcdef");
448        assert_eq!(before_start_anchor.to_offset(&buffer), 0);
449        assert_eq!(after_start_anchor.to_offset(&buffer), 3);
450        assert_eq!(before_end_anchor.to_offset(&buffer), 6);
451        assert_eq!(after_end_anchor.to_offset(&buffer), 9);
452        buffer
453    });
454}
455
456#[gpui::test]
457async fn test_apply_diff(mut cx: gpui::TestAppContext) {
458    let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
459    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
460
461    let text = "a\nccc\ndddd\nffffff\n";
462    let diff = buffer.read_with(&cx, |b, cx| b.diff(text.into(), cx)).await;
463    buffer.update(&mut cx, |b, cx| b.apply_diff(diff, cx));
464    cx.read(|cx| assert_eq!(buffer.read(cx).text(), text));
465
466    let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
467    let diff = buffer.read_with(&cx, |b, cx| b.diff(text.into(), cx)).await;
468    buffer.update(&mut cx, |b, cx| b.apply_diff(diff, cx));
469    cx.read(|cx| assert_eq!(buffer.read(cx).text(), text));
470}
471
472#[gpui::test]
473fn test_undo_redo(cx: &mut gpui::MutableAppContext) {
474    cx.add_model(|cx| {
475        let mut buffer = Buffer::new(0, "1234", cx);
476        // Set group interval to zero so as to not group edits in the undo stack.
477        buffer.history.group_interval = Duration::from_secs(0);
478
479        buffer.edit(vec![1..1], "abx", cx);
480        buffer.edit(vec![3..4], "yzef", cx);
481        buffer.edit(vec![3..5], "cd", cx);
482        assert_eq!(buffer.text(), "1abcdef234");
483
484        let transactions = buffer.history.undo_stack.clone();
485        assert_eq!(transactions.len(), 3);
486
487        buffer.undo_or_redo(transactions[0].clone(), cx).unwrap();
488        assert_eq!(buffer.text(), "1cdef234");
489        buffer.undo_or_redo(transactions[0].clone(), cx).unwrap();
490        assert_eq!(buffer.text(), "1abcdef234");
491
492        buffer.undo_or_redo(transactions[1].clone(), cx).unwrap();
493        assert_eq!(buffer.text(), "1abcdx234");
494        buffer.undo_or_redo(transactions[2].clone(), cx).unwrap();
495        assert_eq!(buffer.text(), "1abx234");
496        buffer.undo_or_redo(transactions[1].clone(), cx).unwrap();
497        assert_eq!(buffer.text(), "1abyzef234");
498        buffer.undo_or_redo(transactions[2].clone(), cx).unwrap();
499        assert_eq!(buffer.text(), "1abcdef234");
500
501        buffer.undo_or_redo(transactions[2].clone(), cx).unwrap();
502        assert_eq!(buffer.text(), "1abyzef234");
503        buffer.undo_or_redo(transactions[0].clone(), cx).unwrap();
504        assert_eq!(buffer.text(), "1yzef234");
505        buffer.undo_or_redo(transactions[1].clone(), cx).unwrap();
506        assert_eq!(buffer.text(), "1234");
507
508        buffer
509    });
510}
511
512#[gpui::test]
513fn test_history(cx: &mut gpui::MutableAppContext) {
514    cx.add_model(|cx| {
515        let mut now = Instant::now();
516        let mut buffer = Buffer::new(0, "123456", cx);
517
518        let set_id =
519            buffer.add_selection_set(buffer.selections_from_ranges(vec![4..4]).unwrap(), cx);
520        buffer.start_transaction_at(Some(set_id), now).unwrap();
521        buffer.edit(vec![2..4], "cd", cx);
522        buffer.end_transaction_at(Some(set_id), now, cx).unwrap();
523        assert_eq!(buffer.text(), "12cd56");
524        assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![4..4]);
525
526        buffer.start_transaction_at(Some(set_id), now).unwrap();
527        buffer
528            .update_selection_set(
529                set_id,
530                buffer.selections_from_ranges(vec![1..3]).unwrap(),
531                cx,
532            )
533            .unwrap();
534        buffer.edit(vec![4..5], "e", cx);
535        buffer.end_transaction_at(Some(set_id), now, cx).unwrap();
536        assert_eq!(buffer.text(), "12cde6");
537        assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![1..3]);
538
539        now += buffer.history.group_interval + Duration::from_millis(1);
540        buffer.start_transaction_at(Some(set_id), now).unwrap();
541        buffer
542            .update_selection_set(
543                set_id,
544                buffer.selections_from_ranges(vec![2..2]).unwrap(),
545                cx,
546            )
547            .unwrap();
548        buffer.edit(vec![0..1], "a", cx);
549        buffer.edit(vec![1..1], "b", cx);
550        buffer.end_transaction_at(Some(set_id), now, cx).unwrap();
551        assert_eq!(buffer.text(), "ab2cde6");
552        assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![3..3]);
553
554        // Last transaction happened past the group interval, undo it on its
555        // own.
556        buffer.undo(cx);
557        assert_eq!(buffer.text(), "12cde6");
558        assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![1..3]);
559
560        // First two transactions happened within the group interval, undo them
561        // together.
562        buffer.undo(cx);
563        assert_eq!(buffer.text(), "123456");
564        assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![4..4]);
565
566        // Redo the first two transactions together.
567        buffer.redo(cx);
568        assert_eq!(buffer.text(), "12cde6");
569        assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![1..3]);
570
571        // Redo the last transaction on its own.
572        buffer.redo(cx);
573        assert_eq!(buffer.text(), "ab2cde6");
574        assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![3..3]);
575
576        buffer.start_transaction_at(None, now).unwrap();
577        buffer.end_transaction_at(None, now, cx).unwrap();
578        buffer.undo(cx);
579        assert_eq!(buffer.text(), "12cde6");
580
581        buffer
582    });
583}
584
585#[gpui::test]
586fn test_concurrent_edits(cx: &mut gpui::MutableAppContext) {
587    let text = "abcdef";
588
589    let buffer1 = cx.add_model(|cx| Buffer::new(1, text, cx));
590    let buffer2 = cx.add_model(|cx| Buffer::new(2, text, cx));
591    let buffer3 = cx.add_model(|cx| Buffer::new(3, text, cx));
592
593    let buf1_op = buffer1.update(cx, |buffer, cx| {
594        buffer.edit(vec![1..2], "12", cx);
595        assert_eq!(buffer.text(), "a12cdef");
596        buffer.operations.last().unwrap().clone()
597    });
598    let buf2_op = buffer2.update(cx, |buffer, cx| {
599        buffer.edit(vec![3..4], "34", cx);
600        assert_eq!(buffer.text(), "abc34ef");
601        buffer.operations.last().unwrap().clone()
602    });
603    let buf3_op = buffer3.update(cx, |buffer, cx| {
604        buffer.edit(vec![5..6], "56", cx);
605        assert_eq!(buffer.text(), "abcde56");
606        buffer.operations.last().unwrap().clone()
607    });
608
609    buffer1.update(cx, |buffer, _| {
610        buffer.apply_op(buf2_op.clone()).unwrap();
611        buffer.apply_op(buf3_op.clone()).unwrap();
612    });
613    buffer2.update(cx, |buffer, _| {
614        buffer.apply_op(buf1_op.clone()).unwrap();
615        buffer.apply_op(buf3_op.clone()).unwrap();
616    });
617    buffer3.update(cx, |buffer, _| {
618        buffer.apply_op(buf1_op.clone()).unwrap();
619        buffer.apply_op(buf2_op.clone()).unwrap();
620    });
621
622    assert_eq!(buffer1.read(cx).text(), "a12c34e56");
623    assert_eq!(buffer2.read(cx).text(), "a12c34e56");
624    assert_eq!(buffer3.read(cx).text(), "a12c34e56");
625}
626
627#[gpui::test(iterations = 100)]
628fn test_random_concurrent_edits(cx: &mut gpui::MutableAppContext, mut rng: StdRng) {
629    let peers = env::var("PEERS")
630        .map(|i| i.parse().expect("invalid `PEERS` variable"))
631        .unwrap_or(5);
632    let operations = env::var("OPERATIONS")
633        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
634        .unwrap_or(10);
635
636    let base_text_len = rng.gen_range(0..10);
637    let base_text = RandomCharIter::new(&mut rng)
638        .take(base_text_len)
639        .collect::<String>();
640    let mut replica_ids = Vec::new();
641    let mut buffers = Vec::new();
642    let mut network = Network::new(rng.clone());
643
644    for i in 0..peers {
645        let buffer = cx.add_model(|cx| {
646            let mut buf = Buffer::new(i as ReplicaId, base_text.as_str(), cx);
647            buf.history.group_interval = Duration::from_millis(rng.gen_range(0..=200));
648            buf
649        });
650        buffers.push(buffer);
651        replica_ids.push(i as u16);
652        network.add_peer(i as u16);
653    }
654
655    log::info!("initial text: {:?}", base_text);
656
657    let mut mutation_count = operations;
658    loop {
659        let replica_index = rng.gen_range(0..peers);
660        let replica_id = replica_ids[replica_index];
661        buffers[replica_index].update(cx, |buffer, cx| match rng.gen_range(0..=100) {
662            0..=50 if mutation_count != 0 => {
663                buffer.randomly_mutate(&mut rng, cx);
664                network.broadcast(buffer.replica_id, mem::take(&mut buffer.operations));
665                log::info!("buffer {} text: {:?}", buffer.replica_id, buffer.text());
666                mutation_count -= 1;
667            }
668            51..=70 if mutation_count != 0 => {
669                buffer.randomly_undo_redo(&mut rng, cx);
670                network.broadcast(buffer.replica_id, mem::take(&mut buffer.operations));
671                mutation_count -= 1;
672            }
673            71..=100 if network.has_unreceived(replica_id) => {
674                let ops = network.receive(replica_id);
675                if !ops.is_empty() {
676                    log::info!(
677                        "peer {} applying {} ops from the network.",
678                        replica_id,
679                        ops.len()
680                    );
681                    buffer.apply_ops(ops, cx).unwrap();
682                }
683            }
684            _ => {}
685        });
686
687        if mutation_count == 0 && network.is_idle() {
688            break;
689        }
690    }
691
692    let first_buffer = buffers[0].read(cx);
693    for buffer in &buffers[1..] {
694        let buffer = buffer.read(cx);
695        assert_eq!(
696            buffer.text(),
697            first_buffer.text(),
698            "Replica {} text != Replica 0 text",
699            buffer.replica_id
700        );
701        assert_eq!(
702            buffer.selection_sets().collect::<HashMap<_, _>>(),
703            first_buffer.selection_sets().collect::<HashMap<_, _>>()
704        );
705        assert_eq!(
706            buffer.all_selection_ranges().collect::<HashMap<_, _>>(),
707            first_buffer
708                .all_selection_ranges()
709                .collect::<HashMap<_, _>>()
710        );
711    }
712}
713
714#[derive(Clone)]
715struct Envelope<T: Clone> {
716    message: T,
717    sender: ReplicaId,
718}
719
720struct Network<T: Clone, R: rand::Rng> {
721    inboxes: std::collections::BTreeMap<ReplicaId, Vec<Envelope<T>>>,
722    all_messages: Vec<T>,
723    rng: R,
724}
725
726impl<T: Clone, R: rand::Rng> Network<T, R> {
727    fn new(rng: R) -> Self {
728        Network {
729            inboxes: Default::default(),
730            all_messages: Vec::new(),
731            rng,
732        }
733    }
734
735    fn add_peer(&mut self, id: ReplicaId) {
736        self.inboxes.insert(id, Vec::new());
737    }
738
739    fn is_idle(&self) -> bool {
740        self.inboxes.values().all(|i| i.is_empty())
741    }
742
743    fn broadcast(&mut self, sender: ReplicaId, messages: Vec<T>) {
744        for (replica, inbox) in self.inboxes.iter_mut() {
745            if *replica != sender {
746                for message in &messages {
747                    let min_index = inbox
748                        .iter()
749                        .enumerate()
750                        .rev()
751                        .find_map(|(index, envelope)| {
752                            if sender == envelope.sender {
753                                Some(index + 1)
754                            } else {
755                                None
756                            }
757                        })
758                        .unwrap_or(0);
759
760                    // Insert one or more duplicates of this message *after* the previous
761                    // message delivered by this replica.
762                    for _ in 0..self.rng.gen_range(1..4) {
763                        let insertion_index = self.rng.gen_range(min_index..inbox.len() + 1);
764                        inbox.insert(
765                            insertion_index,
766                            Envelope {
767                                message: message.clone(),
768                                sender,
769                            },
770                        );
771                    }
772                }
773            }
774        }
775        self.all_messages.extend(messages);
776    }
777
778    fn has_unreceived(&self, receiver: ReplicaId) -> bool {
779        !self.inboxes[&receiver].is_empty()
780    }
781
782    fn receive(&mut self, receiver: ReplicaId) -> Vec<T> {
783        let inbox = self.inboxes.get_mut(&receiver).unwrap();
784        let count = self.rng.gen_range(0..inbox.len() + 1);
785        inbox
786            .drain(0..count)
787            .map(|envelope| envelope.message)
788            .collect()
789    }
790}