tests.rs

  1use super::*;
  2use clock::ReplicaId;
  3use collections::BTreeMap;
  4use gpui::{ModelHandle, MutableAppContext};
  5use rand::prelude::*;
  6use std::{
  7    cell::RefCell,
  8    env,
  9    ops::Range,
 10    rc::Rc,
 11    time::{Duration, Instant},
 12};
 13use text::network::Network;
 14use unindent::Unindent as _;
 15use util::post_inc;
 16
 17#[cfg(test)]
 18#[ctor::ctor]
 19fn init_logger() {
 20    if std::env::var("RUST_LOG").is_ok() {
 21        env_logger::init();
 22    }
 23}
 24
 25#[gpui::test]
 26fn test_select_language() {
 27    let registry = LanguageRegistry::test();
 28    registry.add(Arc::new(Language::new(
 29        LanguageConfig {
 30            name: "Rust".into(),
 31            path_suffixes: vec!["rs".to_string()],
 32            ..Default::default()
 33        },
 34        Some(tree_sitter_rust::language()),
 35    )));
 36    registry.add(Arc::new(Language::new(
 37        LanguageConfig {
 38            name: "Make".into(),
 39            path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
 40            ..Default::default()
 41        },
 42        Some(tree_sitter_rust::language()),
 43    )));
 44
 45    // matching file extension
 46    assert_eq!(
 47        registry.select_language("zed/lib.rs").map(|l| l.name()),
 48        Some("Rust".into())
 49    );
 50    assert_eq!(
 51        registry.select_language("zed/lib.mk").map(|l| l.name()),
 52        Some("Make".into())
 53    );
 54
 55    // matching filename
 56    assert_eq!(
 57        registry.select_language("zed/Makefile").map(|l| l.name()),
 58        Some("Make".into())
 59    );
 60
 61    // matching suffix that is not the full file extension or filename
 62    assert_eq!(registry.select_language("zed/cars").map(|l| l.name()), None);
 63    assert_eq!(
 64        registry.select_language("zed/a.cars").map(|l| l.name()),
 65        None
 66    );
 67    assert_eq!(registry.select_language("zed/sumk").map(|l| l.name()), None);
 68}
 69
 70#[gpui::test]
 71fn test_edit_events(cx: &mut gpui::MutableAppContext) {
 72    let mut now = Instant::now();
 73    let buffer_1_events = Rc::new(RefCell::new(Vec::new()));
 74    let buffer_2_events = Rc::new(RefCell::new(Vec::new()));
 75
 76    let buffer1 = cx.add_model(|cx| Buffer::new(0, "abcdef", cx));
 77    let buffer2 = cx.add_model(|cx| Buffer::new(1, "abcdef", cx));
 78    let buffer1_ops = Rc::new(RefCell::new(Vec::new()));
 79    buffer1.update(cx, {
 80        let buffer1_ops = buffer1_ops.clone();
 81        |buffer, cx| {
 82            let buffer_1_events = buffer_1_events.clone();
 83            cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
 84                Event::Operation(op) => buffer1_ops.borrow_mut().push(op),
 85                event @ _ => buffer_1_events.borrow_mut().push(event),
 86            })
 87            .detach();
 88            let buffer_2_events = buffer_2_events.clone();
 89            cx.subscribe(&buffer2, move |_, _, event, _| {
 90                buffer_2_events.borrow_mut().push(event.clone())
 91            })
 92            .detach();
 93
 94            // An edit emits an edited event, followed by a dirtied event,
 95            // since the buffer was previously in a clean state.
 96            buffer.edit(Some(2..4), "XYZ", cx);
 97
 98            // An empty transaction does not emit any events.
 99            buffer.start_transaction();
100            buffer.end_transaction(cx);
101
102            // A transaction containing two edits emits one edited event.
103            now += Duration::from_secs(1);
104            buffer.start_transaction_at(now);
105            buffer.edit(Some(5..5), "u", cx);
106            buffer.edit(Some(6..6), "w", cx);
107            buffer.end_transaction_at(now, cx);
108
109            // Undoing a transaction emits one edited event.
110            buffer.undo(cx);
111        }
112    });
113
114    // Incorporating a set of remote ops emits a single edited event,
115    // followed by a dirtied event.
116    buffer2.update(cx, |buffer, cx| {
117        buffer
118            .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
119            .unwrap();
120    });
121
122    let buffer_1_events = buffer_1_events.borrow();
123    assert_eq!(
124        *buffer_1_events,
125        vec![Event::Edited, Event::Dirtied, Event::Edited, Event::Edited]
126    );
127
128    let buffer_2_events = buffer_2_events.borrow();
129    assert_eq!(*buffer_2_events, vec![Event::Edited, Event::Dirtied]);
130}
131
132#[gpui::test]
133async fn test_apply_diff(cx: &mut gpui::TestAppContext) {
134    let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
135    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
136
137    let text = "a\nccc\ndddd\nffffff\n";
138    let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
139    buffer.update(cx, |b, cx| b.apply_diff(diff, cx));
140    cx.read(|cx| assert_eq!(buffer.read(cx).text(), text));
141
142    let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
143    let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
144    buffer.update(cx, |b, cx| b.apply_diff(diff, cx));
145    cx.read(|cx| assert_eq!(buffer.read(cx).text(), text));
146}
147
148#[gpui::test]
149async fn test_reparse(cx: &mut gpui::TestAppContext) {
150    let text = "fn a() {}";
151    let buffer =
152        cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
153
154    // Wait for the initial text to parse
155    buffer
156        .condition(&cx, |buffer, _| !buffer.is_parsing())
157        .await;
158    assert_eq!(
159        get_tree_sexp(&buffer, &cx),
160        concat!(
161            "(source_file (function_item name: (identifier) ",
162            "parameters: (parameters) ",
163            "body: (block)))"
164        )
165    );
166
167    buffer.update(cx, |buffer, _| {
168        buffer.set_sync_parse_timeout(Duration::ZERO)
169    });
170
171    // Perform some edits (add parameter and variable reference)
172    // Parsing doesn't begin until the transaction is complete
173    buffer.update(cx, |buf, cx| {
174        buf.start_transaction();
175
176        let offset = buf.text().find(")").unwrap();
177        buf.edit(vec![offset..offset], "b: C", cx);
178        assert!(!buf.is_parsing());
179
180        let offset = buf.text().find("}").unwrap();
181        buf.edit(vec![offset..offset], " d; ", cx);
182        assert!(!buf.is_parsing());
183
184        buf.end_transaction(cx);
185        assert_eq!(buf.text(), "fn a(b: C) { d; }");
186        assert!(buf.is_parsing());
187    });
188    buffer
189        .condition(&cx, |buffer, _| !buffer.is_parsing())
190        .await;
191    assert_eq!(
192        get_tree_sexp(&buffer, &cx),
193        concat!(
194            "(source_file (function_item name: (identifier) ",
195            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
196            "body: (block (expression_statement (identifier)))))"
197        )
198    );
199
200    // Perform a series of edits without waiting for the current parse to complete:
201    // * turn identifier into a field expression
202    // * turn field expression into a method call
203    // * add a turbofish to the method call
204    buffer.update(cx, |buf, cx| {
205        let offset = buf.text().find(";").unwrap();
206        buf.edit(vec![offset..offset], ".e", cx);
207        assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
208        assert!(buf.is_parsing());
209    });
210    buffer.update(cx, |buf, cx| {
211        let offset = buf.text().find(";").unwrap();
212        buf.edit(vec![offset..offset], "(f)", cx);
213        assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
214        assert!(buf.is_parsing());
215    });
216    buffer.update(cx, |buf, cx| {
217        let offset = buf.text().find("(f)").unwrap();
218        buf.edit(vec![offset..offset], "::<G>", cx);
219        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
220        assert!(buf.is_parsing());
221    });
222    buffer
223        .condition(&cx, |buffer, _| !buffer.is_parsing())
224        .await;
225    assert_eq!(
226        get_tree_sexp(&buffer, &cx),
227        concat!(
228            "(source_file (function_item name: (identifier) ",
229            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
230            "body: (block (expression_statement (call_expression ",
231            "function: (generic_function ",
232            "function: (field_expression value: (identifier) field: (field_identifier)) ",
233            "type_arguments: (type_arguments (type_identifier))) ",
234            "arguments: (arguments (identifier)))))))",
235        )
236    );
237
238    buffer.update(cx, |buf, cx| {
239        buf.undo(cx);
240        assert_eq!(buf.text(), "fn a() {}");
241        assert!(buf.is_parsing());
242    });
243    buffer
244        .condition(&cx, |buffer, _| !buffer.is_parsing())
245        .await;
246    assert_eq!(
247        get_tree_sexp(&buffer, &cx),
248        concat!(
249            "(source_file (function_item name: (identifier) ",
250            "parameters: (parameters) ",
251            "body: (block)))"
252        )
253    );
254
255    buffer.update(cx, |buf, cx| {
256        buf.redo(cx);
257        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
258        assert!(buf.is_parsing());
259    });
260    buffer
261        .condition(&cx, |buffer, _| !buffer.is_parsing())
262        .await;
263    assert_eq!(
264        get_tree_sexp(&buffer, &cx),
265        concat!(
266            "(source_file (function_item name: (identifier) ",
267            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
268            "body: (block (expression_statement (call_expression ",
269            "function: (generic_function ",
270            "function: (field_expression value: (identifier) field: (field_identifier)) ",
271            "type_arguments: (type_arguments (type_identifier))) ",
272            "arguments: (arguments (identifier)))))))",
273        )
274    );
275
276    fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
277        buffer.read_with(cx, |buffer, _| {
278            buffer.syntax_tree().unwrap().root_node().to_sexp()
279        })
280    }
281}
282
283#[gpui::test]
284async fn test_outline(cx: &mut gpui::TestAppContext) {
285    let language = Arc::new(
286        rust_lang()
287            .with_outline_query(
288                r#"
289                (struct_item
290                    "struct" @context
291                    name: (_) @name) @item
292                (enum_item
293                    "enum" @context
294                    name: (_) @name) @item
295                (enum_variant
296                    name: (_) @name) @item
297                (field_declaration
298                    name: (_) @name) @item
299                (impl_item
300                    "impl" @context
301                    trait: (_) @name
302                    "for" @context
303                    type: (_) @name) @item
304                (function_item
305                    "fn" @context
306                    name: (_) @name) @item
307                (mod_item
308                    "mod" @context
309                    name: (_) @name) @item
310                "#,
311            )
312            .unwrap(),
313    );
314
315    let text = r#"
316        struct Person {
317            name: String,
318            age: usize,
319        }
320
321        mod module {
322            enum LoginState {
323                LoggedOut,
324                LoggingOn,
325                LoggedIn {
326                    person: Person,
327                    time: Instant,
328                }
329            }
330        }
331
332        impl Eq for Person {}
333
334        impl Drop for Person {
335            fn drop(&mut self) {
336                println!("bye");
337            }
338        }
339    "#
340    .unindent();
341
342    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
343    let outline = buffer
344        .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
345        .unwrap();
346
347    assert_eq!(
348        outline
349            .items
350            .iter()
351            .map(|item| (item.text.as_str(), item.depth))
352            .collect::<Vec<_>>(),
353        &[
354            ("struct Person", 0),
355            ("name", 1),
356            ("age", 1),
357            ("mod module", 0),
358            ("enum LoginState", 1),
359            ("LoggedOut", 2),
360            ("LoggingOn", 2),
361            ("LoggedIn", 2),
362            ("person", 3),
363            ("time", 3),
364            ("impl Eq for Person", 0),
365            ("impl Drop for Person", 0),
366            ("fn drop", 1),
367        ]
368    );
369
370    // Without space, we only match on names
371    assert_eq!(
372        search(&outline, "oon", &cx).await,
373        &[
374            ("mod module", vec![]),                    // included as the parent of a match
375            ("enum LoginState", vec![]),               // included as the parent of a match
376            ("LoggingOn", vec![1, 7, 8]),              // matches
377            ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
378        ]
379    );
380
381    assert_eq!(
382        search(&outline, "dp p", &cx).await,
383        &[
384            ("impl Drop for Person", vec![5, 8, 9, 14]),
385            ("fn drop", vec![]),
386        ]
387    );
388    assert_eq!(
389        search(&outline, "dpn", &cx).await,
390        &[("impl Drop for Person", vec![5, 14, 19])]
391    );
392    assert_eq!(
393        search(&outline, "impl ", &cx).await,
394        &[
395            ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
396            ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
397            ("fn drop", vec![]),
398        ]
399    );
400
401    async fn search<'a>(
402        outline: &'a Outline<Anchor>,
403        query: &str,
404        cx: &gpui::TestAppContext,
405    ) -> Vec<(&'a str, Vec<usize>)> {
406        let matches = cx
407            .read(|cx| outline.search(query, cx.background().clone()))
408            .await;
409        matches
410            .into_iter()
411            .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
412            .collect::<Vec<_>>()
413    }
414}
415
416#[gpui::test]
417fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) {
418    let buffer = cx.add_model(|cx| {
419        let text = "
420            mod x {
421                mod y {
422
423                }
424            }
425        "
426        .unindent();
427        Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx)
428    });
429    let buffer = buffer.read(cx);
430    assert_eq!(
431        buffer.enclosing_bracket_point_ranges(Point::new(1, 6)..Point::new(1, 6)),
432        Some((
433            Point::new(0, 6)..Point::new(0, 7),
434            Point::new(4, 0)..Point::new(4, 1)
435        ))
436    );
437    assert_eq!(
438        buffer.enclosing_bracket_point_ranges(Point::new(1, 10)..Point::new(1, 10)),
439        Some((
440            Point::new(1, 10)..Point::new(1, 11),
441            Point::new(3, 4)..Point::new(3, 5)
442        ))
443    );
444    assert_eq!(
445        buffer.enclosing_bracket_point_ranges(Point::new(3, 5)..Point::new(3, 5)),
446        Some((
447            Point::new(1, 10)..Point::new(1, 11),
448            Point::new(3, 4)..Point::new(3, 5)
449        ))
450    );
451}
452
453#[gpui::test]
454fn test_range_for_syntax_ancestor(cx: &mut MutableAppContext) {
455    cx.add_model(|cx| {
456        let text = "fn a() { b(|c| {}) }";
457        let buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
458        let snapshot = buffer.snapshot();
459
460        assert_eq!(
461            snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
462            Some(range_of(text, "|"))
463        );
464        assert_eq!(
465            snapshot.range_for_syntax_ancestor(range_of(text, "|")),
466            Some(range_of(text, "|c|"))
467        );
468        assert_eq!(
469            snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
470            Some(range_of(text, "|c| {}"))
471        );
472        assert_eq!(
473            snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
474            Some(range_of(text, "(|c| {})"))
475        );
476
477        buffer
478    });
479
480    fn empty_range_at(text: &str, part: &str) -> Range<usize> {
481        let start = text.find(part).unwrap();
482        start..start
483    }
484
485    fn range_of(text: &str, part: &str) -> Range<usize> {
486        let start = text.find(part).unwrap();
487        start..start + part.len()
488    }
489}
490
491#[gpui::test]
492fn test_edit_with_autoindent(cx: &mut MutableAppContext) {
493    cx.add_model(|cx| {
494        let text = "fn a() {}";
495        let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
496
497        buffer.edit_with_autoindent([8..8], "\n\n", cx);
498        assert_eq!(buffer.text(), "fn a() {\n    \n}");
499
500        buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 4)], "b()\n", cx);
501        assert_eq!(buffer.text(), "fn a() {\n    b()\n    \n}");
502
503        buffer.edit_with_autoindent([Point::new(2, 4)..Point::new(2, 4)], ".c", cx);
504        assert_eq!(buffer.text(), "fn a() {\n    b()\n        .c\n}");
505
506        buffer
507    });
508}
509
510#[gpui::test]
511fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
512    cx.add_model(|cx| {
513        let text = "
514            fn a() {
515            c;
516            d;
517            }
518        "
519        .unindent();
520
521        let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
522
523        // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
524        // their indentation is not adjusted.
525        buffer.edit_with_autoindent([empty(Point::new(1, 1)), empty(Point::new(2, 1))], "()", cx);
526        assert_eq!(
527            buffer.text(),
528            "
529            fn a() {
530            c();
531            d();
532            }
533            "
534            .unindent()
535        );
536
537        // When appending new content after these lines, the indentation is based on the
538        // preceding lines' actual indentation.
539        buffer.edit_with_autoindent(
540            [empty(Point::new(1, 1)), empty(Point::new(2, 1))],
541            "\n.f\n.g",
542            cx,
543        );
544        assert_eq!(
545            buffer.text(),
546            "
547            fn a() {
548            c
549                .f
550                .g();
551            d
552                .f
553                .g();
554            }
555            "
556            .unindent()
557        );
558        buffer
559    });
560}
561
562#[gpui::test]
563fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
564    cx.add_model(|cx| {
565        let text = "
566            fn a() {}
567        "
568        .unindent();
569
570        let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
571
572        buffer.edit_with_autoindent([5..5], "\nb", cx);
573        assert_eq!(
574            buffer.text(),
575            "
576                fn a(
577                    b) {}
578            "
579            .unindent()
580        );
581
582        // The indentation suggestion changed because `@end` node (a close paren)
583        // is now at the beginning of the line.
584        buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 5)], "", cx);
585        assert_eq!(
586            buffer.text(),
587            "
588                fn a(
589                ) {}
590            "
591            .unindent()
592        );
593
594        buffer
595    });
596}
597
598#[gpui::test]
599fn test_serialization(cx: &mut gpui::MutableAppContext) {
600    let mut now = Instant::now();
601
602    let buffer1 = cx.add_model(|cx| {
603        let mut buffer = Buffer::new(0, "abc", cx);
604        buffer.edit([3..3], "D", cx);
605
606        now += Duration::from_secs(1);
607        buffer.start_transaction_at(now);
608        buffer.edit([4..4], "E", cx);
609        buffer.end_transaction_at(now, cx);
610        assert_eq!(buffer.text(), "abcDE");
611
612        buffer.undo(cx);
613        assert_eq!(buffer.text(), "abcD");
614
615        buffer.edit([4..4], "F", cx);
616        assert_eq!(buffer.text(), "abcDF");
617        buffer
618    });
619    assert_eq!(buffer1.read(cx).text(), "abcDF");
620
621    let message = buffer1.read(cx).to_proto();
622    let buffer2 = cx.add_model(|cx| Buffer::from_proto(1, message, None, cx).unwrap());
623    assert_eq!(buffer2.read(cx).text(), "abcDF");
624}
625
626#[gpui::test(iterations = 100)]
627fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
628    let min_peers = env::var("MIN_PEERS")
629        .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
630        .unwrap_or(1);
631    let max_peers = env::var("MAX_PEERS")
632        .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
633        .unwrap_or(5);
634    let operations = env::var("OPERATIONS")
635        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
636        .unwrap_or(10);
637
638    let base_text_len = rng.gen_range(0..10);
639    let base_text = RandomCharIter::new(&mut rng)
640        .take(base_text_len)
641        .collect::<String>();
642    let mut replica_ids = Vec::new();
643    let mut buffers = Vec::new();
644    let network = Rc::new(RefCell::new(Network::new(rng.clone())));
645
646    for i in 0..rng.gen_range(min_peers..=max_peers) {
647        let buffer = cx.add_model(|cx| {
648            let mut buffer = Buffer::new(i as ReplicaId, base_text.as_str(), cx);
649            buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
650            let network = network.clone();
651            cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
652                if let Event::Operation(op) = event {
653                    network
654                        .borrow_mut()
655                        .broadcast(buffer.replica_id(), vec![proto::serialize_operation(&op)]);
656                }
657            })
658            .detach();
659            buffer
660        });
661        buffers.push(buffer);
662        replica_ids.push(i as ReplicaId);
663        network.borrow_mut().add_peer(i as ReplicaId);
664        log::info!("Adding initial peer with replica id {}", i);
665    }
666
667    log::info!("initial text: {:?}", base_text);
668
669    let mut now = Instant::now();
670    let mut mutation_count = operations;
671    let mut next_diagnostic_id = 0;
672    let mut active_selections = BTreeMap::default();
673    loop {
674        let replica_index = rng.gen_range(0..replica_ids.len());
675        let replica_id = replica_ids[replica_index];
676        let buffer = &mut buffers[replica_index];
677        let mut new_buffer = None;
678        match rng.gen_range(0..100) {
679            0..=29 if mutation_count != 0 => {
680                buffer.update(cx, |buffer, cx| {
681                    buffer.start_transaction_at(now);
682                    buffer.randomly_edit(&mut rng, 5, cx);
683                    buffer.end_transaction_at(now, cx);
684                    log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
685                });
686                mutation_count -= 1;
687            }
688            30..=39 if mutation_count != 0 => {
689                buffer.update(cx, |buffer, cx| {
690                    let mut selections = Vec::new();
691                    for id in 0..rng.gen_range(1..=5) {
692                        let range = buffer.random_byte_range(0, &mut rng);
693                        selections.push(Selection {
694                            id,
695                            start: buffer.anchor_before(range.start),
696                            end: buffer.anchor_before(range.end),
697                            reversed: false,
698                            goal: SelectionGoal::None,
699                        });
700                    }
701                    let selections: Arc<[Selection<Anchor>]> = selections.into();
702                    log::info!(
703                        "peer {} setting active selections: {:?}",
704                        replica_id,
705                        selections
706                    );
707                    active_selections.insert(replica_id, selections.clone());
708                    buffer.set_active_selections(selections, cx);
709                });
710                mutation_count -= 1;
711            }
712            40..=49 if mutation_count != 0 && replica_id == 0 => {
713                let entry_count = rng.gen_range(1..=5);
714                buffer.update(cx, |buffer, cx| {
715                    let diagnostics = DiagnosticSet::new(
716                        (0..entry_count).map(|_| {
717                            let range = buffer.random_byte_range(0, &mut rng);
718                            let range = range.to_point_utf16(buffer);
719                            DiagnosticEntry {
720                                range,
721                                diagnostic: Diagnostic {
722                                    message: post_inc(&mut next_diagnostic_id).to_string(),
723                                    ..Default::default()
724                                },
725                            }
726                        }),
727                        buffer,
728                    );
729                    log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
730                    buffer.update_diagnostics(diagnostics, cx);
731                });
732                mutation_count -= 1;
733            }
734            50..=59 if replica_ids.len() < max_peers => {
735                let old_buffer = buffer.read(cx).to_proto();
736                let new_replica_id = replica_ids.len() as ReplicaId;
737                log::info!(
738                    "Adding new replica {} (replicating from {})",
739                    new_replica_id,
740                    replica_id
741                );
742                new_buffer = Some(cx.add_model(|cx| {
743                    let mut new_buffer =
744                        Buffer::from_proto(new_replica_id, old_buffer, None, cx).unwrap();
745                    new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
746                    let network = network.clone();
747                    cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
748                        if let Event::Operation(op) = event {
749                            network.borrow_mut().broadcast(
750                                buffer.replica_id(),
751                                vec![proto::serialize_operation(&op)],
752                            );
753                        }
754                    })
755                    .detach();
756                    new_buffer
757                }));
758                replica_ids.push(new_replica_id);
759                network.borrow_mut().replicate(replica_id, new_replica_id);
760            }
761            60..=69 if mutation_count != 0 => {
762                buffer.update(cx, |buffer, cx| {
763                    buffer.randomly_undo_redo(&mut rng, cx);
764                    log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
765                });
766                mutation_count -= 1;
767            }
768            _ if network.borrow().has_unreceived(replica_id) => {
769                let ops = network
770                    .borrow_mut()
771                    .receive(replica_id)
772                    .into_iter()
773                    .map(|op| proto::deserialize_operation(op).unwrap());
774                if ops.len() > 0 {
775                    log::info!(
776                        "peer {} applying {} ops from the network.",
777                        replica_id,
778                        ops.len()
779                    );
780                    buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
781                }
782            }
783            _ => {}
784        }
785
786        now += Duration::from_millis(rng.gen_range(0..=200));
787        buffers.extend(new_buffer);
788
789        for buffer in &buffers {
790            buffer.read(cx).check_invariants();
791        }
792
793        if mutation_count == 0 && network.borrow().is_idle() {
794            break;
795        }
796    }
797
798    let first_buffer = buffers[0].read(cx).snapshot();
799    for buffer in &buffers[1..] {
800        let buffer = buffer.read(cx).snapshot();
801        assert_eq!(
802            buffer.text(),
803            first_buffer.text(),
804            "Replica {} text != Replica 0 text",
805            buffer.replica_id()
806        );
807        assert_eq!(
808            buffer
809                .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
810                .collect::<Vec<_>>(),
811            first_buffer
812                .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
813                .collect::<Vec<_>>(),
814            "Replica {} diagnostics != Replica 0 diagnostics",
815            buffer.replica_id()
816        );
817    }
818
819    for buffer in &buffers {
820        let buffer = buffer.read(cx).snapshot();
821        let actual_remote_selections = buffer
822            .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
823            .map(|(replica_id, selections)| (replica_id, selections.collect::<Vec<_>>()))
824            .collect::<Vec<_>>();
825        let expected_remote_selections = active_selections
826            .iter()
827            .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
828            .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
829            .collect::<Vec<_>>();
830        assert_eq!(actual_remote_selections, expected_remote_selections);
831    }
832}
833
834#[test]
835fn test_contiguous_ranges() {
836    assert_eq!(
837        contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
838        &[1..4, 5..7, 9..13]
839    );
840
841    // Respects the `max_len` parameter
842    assert_eq!(
843        contiguous_ranges(
844            [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
845            3
846        )
847        .collect::<Vec<_>>(),
848        &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
849    );
850}
851
852impl Buffer {
853    pub fn enclosing_bracket_point_ranges<T: ToOffset>(
854        &self,
855        range: Range<T>,
856    ) -> Option<(Range<Point>, Range<Point>)> {
857        self.snapshot()
858            .enclosing_bracket_ranges(range)
859            .map(|(start, end)| {
860                let point_start = start.start.to_point(self)..start.end.to_point(self);
861                let point_end = end.start.to_point(self)..end.end.to_point(self);
862                (point_start, point_end)
863            })
864    }
865}
866
867fn rust_lang() -> Language {
868    Language::new(
869        LanguageConfig {
870            name: "Rust".into(),
871            path_suffixes: vec!["rs".to_string()],
872            language_server: None,
873            ..Default::default()
874        },
875        Some(tree_sitter_rust::language()),
876    )
877    .with_indents_query(
878        r#"
879        (call_expression) @indent
880        (field_expression) @indent
881        (_ "(" ")" @end) @indent
882        (_ "{" "}" @end) @indent
883        "#,
884    )
885    .unwrap()
886    .with_brackets_query(
887        r#"
888        ("{" @open "}" @close)
889        "#,
890    )
891    .unwrap()
892}
893
894fn empty(point: Point) -> Range<Point> {
895    point..point
896}