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}