tests.rs

  1use super::*;
  2use clock::ReplicaId;
  3use rand::prelude::*;
  4use std::{
  5    cmp::Ordering,
  6    env,
  7    iter::Iterator,
  8    time::{Duration, Instant},
  9};
 10use util::test::Network;
 11
 12#[cfg(test)]
 13#[ctor::ctor]
 14fn init_logger() {
 15    if std::env::var("RUST_LOG").is_ok() {
 16        env_logger::init();
 17    }
 18}
 19
 20#[test]
 21fn test_edit() {
 22    let mut buffer = Buffer::new(0, 0, History::new("abc".into()));
 23    assert_eq!(buffer.text(), "abc");
 24    buffer.edit(vec![3..3], "def");
 25    assert_eq!(buffer.text(), "abcdef");
 26    buffer.edit(vec![0..0], "ghi");
 27    assert_eq!(buffer.text(), "ghiabcdef");
 28    buffer.edit(vec![5..5], "jkl");
 29    assert_eq!(buffer.text(), "ghiabjklcdef");
 30    buffer.edit(vec![6..7], "");
 31    assert_eq!(buffer.text(), "ghiabjlcdef");
 32    buffer.edit(vec![4..9], "mno");
 33    assert_eq!(buffer.text(), "ghiamnoef");
 34}
 35
 36#[gpui::test(iterations = 100)]
 37fn test_random_edits(mut rng: StdRng) {
 38    let operations = env::var("OPERATIONS")
 39        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
 40        .unwrap_or(10);
 41
 42    let reference_string_len = rng.gen_range(0..3);
 43    let mut reference_string = RandomCharIter::new(&mut rng)
 44        .take(reference_string_len)
 45        .collect::<String>();
 46    let mut buffer = Buffer::new(0, 0, History::new(reference_string.clone().into()));
 47    buffer.history.group_interval = Duration::from_millis(rng.gen_range(0..=200));
 48    let mut buffer_versions = Vec::new();
 49    log::info!(
 50        "buffer text {:?}, version: {:?}",
 51        buffer.text(),
 52        buffer.version()
 53    );
 54
 55    for _i in 0..operations {
 56        let (old_ranges, new_text, _) = buffer.randomly_edit(&mut rng, 5);
 57        for old_range in old_ranges.iter().rev() {
 58            reference_string.replace_range(old_range.clone(), &new_text);
 59        }
 60        assert_eq!(buffer.text(), reference_string);
 61        log::info!(
 62            "buffer text {:?}, version: {:?}",
 63            buffer.text(),
 64            buffer.version()
 65        );
 66
 67        if rng.gen_bool(0.25) {
 68            buffer.randomly_undo_redo(&mut rng);
 69            reference_string = buffer.text();
 70            log::info!(
 71                "buffer text {:?}, version: {:?}",
 72                buffer.text(),
 73                buffer.version()
 74            );
 75        }
 76
 77        let range = buffer.random_byte_range(0, &mut rng);
 78        assert_eq!(
 79            buffer.text_summary_for_range::<TextSummary, _>(range.clone()),
 80            TextSummary::from(&reference_string[range])
 81        );
 82
 83        buffer.check_invariants();
 84
 85        if rng.gen_bool(0.3) {
 86            buffer_versions.push((buffer.clone(), buffer.subscribe()));
 87        }
 88    }
 89
 90    for (old_buffer, subscription) in buffer_versions {
 91        let edits = buffer
 92            .edits_since::<usize>(&old_buffer.version)
 93            .collect::<Vec<_>>();
 94
 95        log::info!(
 96            "applying edits since version {:?} to old text: {:?}: {:?}",
 97            old_buffer.version(),
 98            old_buffer.text(),
 99            edits,
100        );
101
102        let mut text = old_buffer.visible_text.clone();
103        for edit in edits {
104            let new_text: String = buffer.text_for_range(edit.new.clone()).collect();
105            text.replace(edit.new.start..edit.new.start + edit.old.len(), &new_text);
106        }
107        assert_eq!(text.to_string(), buffer.text());
108
109        for _ in 0..5 {
110            let end_ix = old_buffer.clip_offset(rng.gen_range(0..=old_buffer.len()), Bias::Right);
111            let start_ix = old_buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
112            let range = old_buffer.anchor_before(start_ix)..old_buffer.anchor_after(end_ix);
113            let mut old_text = old_buffer.text_for_range(range.clone()).collect::<String>();
114            let edits = buffer
115                .edits_since_in_range::<usize>(&old_buffer.version, range.clone())
116                .collect::<Vec<_>>();
117            log::info!(
118                "applying edits since version {:?} to old text in range {:?}: {:?}: {:?}",
119                old_buffer.version(),
120                start_ix..end_ix,
121                old_text,
122                edits,
123            );
124
125            let new_text = buffer.text_for_range(range).collect::<String>();
126            for edit in edits {
127                old_text.replace_range(
128                    edit.new.start..edit.new.start + edit.old_len(),
129                    &new_text[edit.new],
130                );
131            }
132            assert_eq!(old_text, new_text);
133        }
134
135        let subscription_edits = subscription.consume();
136        log::info!(
137            "applying subscription edits since version {:?} to old text: {:?}: {:?}",
138            old_buffer.version(),
139            old_buffer.text(),
140            subscription_edits,
141        );
142
143        let mut text = old_buffer.visible_text.clone();
144        for edit in subscription_edits.into_inner() {
145            let new_text: String = buffer.text_for_range(edit.new.clone()).collect();
146            text.replace(edit.new.start..edit.new.start + edit.old.len(), &new_text);
147        }
148        assert_eq!(text.to_string(), buffer.text());
149    }
150}
151
152#[test]
153fn test_line_len() {
154    let mut buffer = Buffer::new(0, 0, History::new("".into()));
155    buffer.edit(vec![0..0], "abcd\nefg\nhij");
156    buffer.edit(vec![12..12], "kl\nmno");
157    buffer.edit(vec![18..18], "\npqrs\n");
158    buffer.edit(vec![18..21], "\nPQ");
159
160    assert_eq!(buffer.line_len(0), 4);
161    assert_eq!(buffer.line_len(1), 3);
162    assert_eq!(buffer.line_len(2), 5);
163    assert_eq!(buffer.line_len(3), 3);
164    assert_eq!(buffer.line_len(4), 4);
165    assert_eq!(buffer.line_len(5), 0);
166}
167
168#[test]
169fn test_text_summary_for_range() {
170    let buffer = Buffer::new(0, 0, History::new("ab\nefg\nhklm\nnopqrs\ntuvwxyz".into()));
171    assert_eq!(
172        buffer.text_summary_for_range::<TextSummary, _>(1..3),
173        TextSummary {
174            bytes: 2,
175            lines: Point::new(1, 0),
176            lines_utf16: PointUtf16::new(1, 0),
177            first_line_chars: 1,
178            last_line_chars: 0,
179            longest_row: 0,
180            longest_row_chars: 1,
181        }
182    );
183    assert_eq!(
184        buffer.text_summary_for_range::<TextSummary, _>(1..12),
185        TextSummary {
186            bytes: 11,
187            lines: Point::new(3, 0),
188            lines_utf16: PointUtf16::new(3, 0),
189            first_line_chars: 1,
190            last_line_chars: 0,
191            longest_row: 2,
192            longest_row_chars: 4,
193        }
194    );
195    assert_eq!(
196        buffer.text_summary_for_range::<TextSummary, _>(0..20),
197        TextSummary {
198            bytes: 20,
199            lines: Point::new(4, 1),
200            lines_utf16: PointUtf16::new(4, 1),
201            first_line_chars: 2,
202            last_line_chars: 1,
203            longest_row: 3,
204            longest_row_chars: 6,
205        }
206    );
207    assert_eq!(
208        buffer.text_summary_for_range::<TextSummary, _>(0..22),
209        TextSummary {
210            bytes: 22,
211            lines: Point::new(4, 3),
212            lines_utf16: PointUtf16::new(4, 3),
213            first_line_chars: 2,
214            last_line_chars: 3,
215            longest_row: 3,
216            longest_row_chars: 6,
217        }
218    );
219    assert_eq!(
220        buffer.text_summary_for_range::<TextSummary, _>(7..22),
221        TextSummary {
222            bytes: 15,
223            lines: Point::new(2, 3),
224            lines_utf16: PointUtf16::new(2, 3),
225            first_line_chars: 4,
226            last_line_chars: 3,
227            longest_row: 1,
228            longest_row_chars: 6,
229        }
230    );
231}
232
233#[test]
234fn test_chars_at() {
235    let mut buffer = Buffer::new(0, 0, History::new("".into()));
236    buffer.edit(vec![0..0], "abcd\nefgh\nij");
237    buffer.edit(vec![12..12], "kl\nmno");
238    buffer.edit(vec![18..18], "\npqrs");
239    buffer.edit(vec![18..21], "\nPQ");
240
241    let chars = buffer.chars_at(Point::new(0, 0));
242    assert_eq!(chars.collect::<String>(), "abcd\nefgh\nijkl\nmno\nPQrs");
243
244    let chars = buffer.chars_at(Point::new(1, 0));
245    assert_eq!(chars.collect::<String>(), "efgh\nijkl\nmno\nPQrs");
246
247    let chars = buffer.chars_at(Point::new(2, 0));
248    assert_eq!(chars.collect::<String>(), "ijkl\nmno\nPQrs");
249
250    let chars = buffer.chars_at(Point::new(3, 0));
251    assert_eq!(chars.collect::<String>(), "mno\nPQrs");
252
253    let chars = buffer.chars_at(Point::new(4, 0));
254    assert_eq!(chars.collect::<String>(), "PQrs");
255
256    // Regression test:
257    let mut buffer = Buffer::new(0, 0, History::new("".into()));
258    buffer.edit(vec![0..0], "[workspace]\nmembers = [\n    \"xray_core\",\n    \"xray_server\",\n    \"xray_cli\",\n    \"xray_wasm\",\n]\n");
259    buffer.edit(vec![60..60], "\n");
260
261    let chars = buffer.chars_at(Point::new(6, 0));
262    assert_eq!(chars.collect::<String>(), "    \"xray_wasm\",\n]\n");
263}
264
265#[test]
266fn test_anchors() {
267    let mut buffer = Buffer::new(0, 0, History::new("".into()));
268    buffer.edit(vec![0..0], "abc");
269    let left_anchor = buffer.anchor_before(2);
270    let right_anchor = buffer.anchor_after(2);
271
272    buffer.edit(vec![1..1], "def\n");
273    assert_eq!(buffer.text(), "adef\nbc");
274    assert_eq!(left_anchor.to_offset(&buffer), 6);
275    assert_eq!(right_anchor.to_offset(&buffer), 6);
276    assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
277    assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
278
279    buffer.edit(vec![2..3], "");
280    assert_eq!(buffer.text(), "adf\nbc");
281    assert_eq!(left_anchor.to_offset(&buffer), 5);
282    assert_eq!(right_anchor.to_offset(&buffer), 5);
283    assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
284    assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
285
286    buffer.edit(vec![5..5], "ghi\n");
287    assert_eq!(buffer.text(), "adf\nbghi\nc");
288    assert_eq!(left_anchor.to_offset(&buffer), 5);
289    assert_eq!(right_anchor.to_offset(&buffer), 9);
290    assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
291    assert_eq!(right_anchor.to_point(&buffer), Point { row: 2, column: 0 });
292
293    buffer.edit(vec![7..9], "");
294    assert_eq!(buffer.text(), "adf\nbghc");
295    assert_eq!(left_anchor.to_offset(&buffer), 5);
296    assert_eq!(right_anchor.to_offset(&buffer), 7);
297    assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 },);
298    assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 3 });
299
300    // Ensure anchoring to a point is equivalent to anchoring to an offset.
301    assert_eq!(
302        buffer.anchor_before(Point { row: 0, column: 0 }),
303        buffer.anchor_before(0)
304    );
305    assert_eq!(
306        buffer.anchor_before(Point { row: 0, column: 1 }),
307        buffer.anchor_before(1)
308    );
309    assert_eq!(
310        buffer.anchor_before(Point { row: 0, column: 2 }),
311        buffer.anchor_before(2)
312    );
313    assert_eq!(
314        buffer.anchor_before(Point { row: 0, column: 3 }),
315        buffer.anchor_before(3)
316    );
317    assert_eq!(
318        buffer.anchor_before(Point { row: 1, column: 0 }),
319        buffer.anchor_before(4)
320    );
321    assert_eq!(
322        buffer.anchor_before(Point { row: 1, column: 1 }),
323        buffer.anchor_before(5)
324    );
325    assert_eq!(
326        buffer.anchor_before(Point { row: 1, column: 2 }),
327        buffer.anchor_before(6)
328    );
329    assert_eq!(
330        buffer.anchor_before(Point { row: 1, column: 3 }),
331        buffer.anchor_before(7)
332    );
333    assert_eq!(
334        buffer.anchor_before(Point { row: 1, column: 4 }),
335        buffer.anchor_before(8)
336    );
337
338    // Comparison between anchors.
339    let anchor_at_offset_0 = buffer.anchor_before(0);
340    let anchor_at_offset_1 = buffer.anchor_before(1);
341    let anchor_at_offset_2 = buffer.anchor_before(2);
342
343    assert_eq!(
344        anchor_at_offset_0
345            .cmp(&anchor_at_offset_0, &buffer)
346            .unwrap(),
347        Ordering::Equal
348    );
349    assert_eq!(
350        anchor_at_offset_1
351            .cmp(&anchor_at_offset_1, &buffer)
352            .unwrap(),
353        Ordering::Equal
354    );
355    assert_eq!(
356        anchor_at_offset_2
357            .cmp(&anchor_at_offset_2, &buffer)
358            .unwrap(),
359        Ordering::Equal
360    );
361
362    assert_eq!(
363        anchor_at_offset_0
364            .cmp(&anchor_at_offset_1, &buffer)
365            .unwrap(),
366        Ordering::Less
367    );
368    assert_eq!(
369        anchor_at_offset_1
370            .cmp(&anchor_at_offset_2, &buffer)
371            .unwrap(),
372        Ordering::Less
373    );
374    assert_eq!(
375        anchor_at_offset_0
376            .cmp(&anchor_at_offset_2, &buffer)
377            .unwrap(),
378        Ordering::Less
379    );
380
381    assert_eq!(
382        anchor_at_offset_1
383            .cmp(&anchor_at_offset_0, &buffer)
384            .unwrap(),
385        Ordering::Greater
386    );
387    assert_eq!(
388        anchor_at_offset_2
389            .cmp(&anchor_at_offset_1, &buffer)
390            .unwrap(),
391        Ordering::Greater
392    );
393    assert_eq!(
394        anchor_at_offset_2
395            .cmp(&anchor_at_offset_0, &buffer)
396            .unwrap(),
397        Ordering::Greater
398    );
399}
400
401#[test]
402fn test_anchors_at_start_and_end() {
403    let mut buffer = Buffer::new(0, 0, History::new("".into()));
404    let before_start_anchor = buffer.anchor_before(0);
405    let after_end_anchor = buffer.anchor_after(0);
406
407    buffer.edit(vec![0..0], "abc");
408    assert_eq!(buffer.text(), "abc");
409    assert_eq!(before_start_anchor.to_offset(&buffer), 0);
410    assert_eq!(after_end_anchor.to_offset(&buffer), 3);
411
412    let after_start_anchor = buffer.anchor_after(0);
413    let before_end_anchor = buffer.anchor_before(3);
414
415    buffer.edit(vec![3..3], "def");
416    buffer.edit(vec![0..0], "ghi");
417    assert_eq!(buffer.text(), "ghiabcdef");
418    assert_eq!(before_start_anchor.to_offset(&buffer), 0);
419    assert_eq!(after_start_anchor.to_offset(&buffer), 3);
420    assert_eq!(before_end_anchor.to_offset(&buffer), 6);
421    assert_eq!(after_end_anchor.to_offset(&buffer), 9);
422}
423
424#[test]
425fn test_undo_redo() {
426    let mut buffer = Buffer::new(0, 0, History::new("1234".into()));
427    // Set group interval to zero so as to not group edits in the undo stack.
428    buffer.history.group_interval = Duration::from_secs(0);
429
430    buffer.edit(vec![1..1], "abx");
431    buffer.edit(vec![3..4], "yzef");
432    buffer.edit(vec![3..5], "cd");
433    assert_eq!(buffer.text(), "1abcdef234");
434
435    let entries = buffer.history.undo_stack.clone();
436    assert_eq!(entries.len(), 3);
437
438    buffer.undo_or_redo(entries[0].transaction.clone()).unwrap();
439    assert_eq!(buffer.text(), "1cdef234");
440    buffer.undo_or_redo(entries[0].transaction.clone()).unwrap();
441    assert_eq!(buffer.text(), "1abcdef234");
442
443    buffer.undo_or_redo(entries[1].transaction.clone()).unwrap();
444    assert_eq!(buffer.text(), "1abcdx234");
445    buffer.undo_or_redo(entries[2].transaction.clone()).unwrap();
446    assert_eq!(buffer.text(), "1abx234");
447    buffer.undo_or_redo(entries[1].transaction.clone()).unwrap();
448    assert_eq!(buffer.text(), "1abyzef234");
449    buffer.undo_or_redo(entries[2].transaction.clone()).unwrap();
450    assert_eq!(buffer.text(), "1abcdef234");
451
452    buffer.undo_or_redo(entries[2].transaction.clone()).unwrap();
453    assert_eq!(buffer.text(), "1abyzef234");
454    buffer.undo_or_redo(entries[0].transaction.clone()).unwrap();
455    assert_eq!(buffer.text(), "1yzef234");
456    buffer.undo_or_redo(entries[1].transaction.clone()).unwrap();
457    assert_eq!(buffer.text(), "1234");
458}
459
460#[test]
461fn test_history() {
462    let mut now = Instant::now();
463    let mut buffer = Buffer::new(0, 0, History::new("123456".into()));
464
465    buffer.start_transaction_at(now);
466    buffer.edit(vec![2..4], "cd");
467    buffer.end_transaction_at(now);
468    assert_eq!(buffer.text(), "12cd56");
469
470    buffer.start_transaction_at(now);
471    buffer.edit(vec![4..5], "e");
472    buffer.end_transaction_at(now).unwrap();
473    assert_eq!(buffer.text(), "12cde6");
474
475    now += buffer.history.group_interval + Duration::from_millis(1);
476    buffer.start_transaction_at(now);
477    buffer.edit(vec![0..1], "a");
478    buffer.edit(vec![1..1], "b");
479    buffer.end_transaction_at(now).unwrap();
480    assert_eq!(buffer.text(), "ab2cde6");
481
482    // Last transaction happened past the group interval, undo it on its own.
483    buffer.undo();
484    assert_eq!(buffer.text(), "12cde6");
485
486    // First two transactions happened within the group interval, undo them together.
487    buffer.undo();
488    assert_eq!(buffer.text(), "123456");
489
490    // Redo the first two transactions together.
491    buffer.redo();
492    assert_eq!(buffer.text(), "12cde6");
493
494    // Redo the last transaction on its own.
495    buffer.redo();
496    assert_eq!(buffer.text(), "ab2cde6");
497
498    buffer.start_transaction_at(now);
499    assert!(buffer.end_transaction_at(now).is_none());
500    buffer.undo();
501    assert_eq!(buffer.text(), "12cde6");
502}
503
504#[test]
505fn test_finalize_last_transaction() {
506    let now = Instant::now();
507    let mut buffer = Buffer::new(0, 0, History::new("123456".into()));
508
509    buffer.start_transaction_at(now);
510    buffer.edit(vec![2..4], "cd");
511    buffer.end_transaction_at(now);
512    assert_eq!(buffer.text(), "12cd56");
513
514    buffer.finalize_last_transaction();
515    buffer.start_transaction_at(now);
516    buffer.edit(vec![4..5], "e");
517    buffer.end_transaction_at(now).unwrap();
518    assert_eq!(buffer.text(), "12cde6");
519
520    buffer.start_transaction_at(now);
521    buffer.edit(vec![0..1], "a");
522    buffer.edit(vec![1..1], "b");
523    buffer.end_transaction_at(now).unwrap();
524    assert_eq!(buffer.text(), "ab2cde6");
525
526    buffer.undo();
527    assert_eq!(buffer.text(), "12cd56");
528
529    buffer.undo();
530    assert_eq!(buffer.text(), "123456");
531
532    buffer.redo();
533    assert_eq!(buffer.text(), "12cd56");
534
535    buffer.redo();
536    assert_eq!(buffer.text(), "ab2cde6");
537}
538
539#[test]
540fn test_edited_ranges_for_transaction() {
541    let now = Instant::now();
542    let mut buffer = Buffer::new(0, 0, History::new("1234567".into()));
543
544    buffer.start_transaction_at(now);
545    buffer.edit(vec![2..4], "cd");
546    buffer.edit(vec![6..6], "efg");
547    buffer.end_transaction_at(now);
548    assert_eq!(buffer.text(), "12cd56efg7");
549
550    let tx = buffer.finalize_last_transaction().unwrap().clone();
551    assert_eq!(
552        buffer
553            .edited_ranges_for_transaction::<usize>(&tx)
554            .collect::<Vec<_>>(),
555        [2..4, 6..9]
556    );
557
558    buffer.edit(vec![5..5], "hijk");
559    assert_eq!(buffer.text(), "12cd5hijk6efg7");
560    assert_eq!(
561        buffer
562            .edited_ranges_for_transaction::<usize>(&tx)
563            .collect::<Vec<_>>(),
564        [2..4, 10..13]
565    );
566
567    buffer.edit(vec![4..4], "l");
568    assert_eq!(buffer.text(), "12cdl5hijk6efg7");
569    assert_eq!(
570        buffer
571            .edited_ranges_for_transaction::<usize>(&tx)
572            .collect::<Vec<_>>(),
573        [2..4, 11..14]
574    );
575}
576
577#[test]
578fn test_concurrent_edits() {
579    let text = "abcdef";
580
581    let mut buffer1 = Buffer::new(1, 0, History::new(text.into()));
582    let mut buffer2 = Buffer::new(2, 0, History::new(text.into()));
583    let mut buffer3 = Buffer::new(3, 0, History::new(text.into()));
584
585    let buf1_op = buffer1.edit(vec![1..2], "12");
586    assert_eq!(buffer1.text(), "a12cdef");
587    let buf2_op = buffer2.edit(vec![3..4], "34");
588    assert_eq!(buffer2.text(), "abc34ef");
589    let buf3_op = buffer3.edit(vec![5..6], "56");
590    assert_eq!(buffer3.text(), "abcde56");
591
592    buffer1.apply_op(buf2_op.clone()).unwrap();
593    buffer1.apply_op(buf3_op.clone()).unwrap();
594    buffer2.apply_op(buf1_op.clone()).unwrap();
595    buffer2.apply_op(buf3_op.clone()).unwrap();
596    buffer3.apply_op(buf1_op.clone()).unwrap();
597    buffer3.apply_op(buf2_op.clone()).unwrap();
598
599    assert_eq!(buffer1.text(), "a12c34e56");
600    assert_eq!(buffer2.text(), "a12c34e56");
601    assert_eq!(buffer3.text(), "a12c34e56");
602}
603
604#[gpui::test(iterations = 100)]
605fn test_random_concurrent_edits(mut rng: StdRng) {
606    let peers = env::var("PEERS")
607        .map(|i| i.parse().expect("invalid `PEERS` variable"))
608        .unwrap_or(5);
609    let operations = env::var("OPERATIONS")
610        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
611        .unwrap_or(10);
612
613    let base_text_len = rng.gen_range(0..10);
614    let base_text = RandomCharIter::new(&mut rng)
615        .take(base_text_len)
616        .collect::<String>();
617    let mut replica_ids = Vec::new();
618    let mut buffers = Vec::new();
619    let mut network = Network::new(rng.clone());
620
621    for i in 0..peers {
622        let mut buffer = Buffer::new(i as ReplicaId, 0, History::new(base_text.clone().into()));
623        buffer.history.group_interval = Duration::from_millis(rng.gen_range(0..=200));
624        buffers.push(buffer);
625        replica_ids.push(i as u16);
626        network.add_peer(i as u16);
627    }
628
629    log::info!("initial text: {:?}", base_text);
630
631    let mut mutation_count = operations;
632    loop {
633        let replica_index = rng.gen_range(0..peers);
634        let replica_id = replica_ids[replica_index];
635        let buffer = &mut buffers[replica_index];
636        match rng.gen_range(0..=100) {
637            0..=50 if mutation_count != 0 => {
638                let op = buffer.randomly_edit(&mut rng, 5).2;
639                network.broadcast(buffer.replica_id, vec![op]);
640                log::info!("buffer {} text: {:?}", buffer.replica_id, buffer.text());
641                mutation_count -= 1;
642            }
643            51..=70 if mutation_count != 0 => {
644                let ops = buffer.randomly_undo_redo(&mut rng);
645                network.broadcast(buffer.replica_id, ops);
646                mutation_count -= 1;
647            }
648            71..=100 if network.has_unreceived(replica_id) => {
649                let ops = network.receive(replica_id);
650                if !ops.is_empty() {
651                    log::info!(
652                        "peer {} applying {} ops from the network.",
653                        replica_id,
654                        ops.len()
655                    );
656                    buffer.apply_ops(ops).unwrap();
657                }
658            }
659            _ => {}
660        }
661        buffer.check_invariants();
662
663        if mutation_count == 0 && network.is_idle() {
664            break;
665        }
666    }
667
668    let first_buffer = &buffers[0];
669    for buffer in &buffers[1..] {
670        assert_eq!(
671            buffer.text(),
672            first_buffer.text(),
673            "Replica {} text != Replica 0 text",
674            buffer.replica_id
675        );
676        buffer.check_invariants();
677    }
678}