tests.rs

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