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}