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_edit_with_autoindent(cx: &mut MutableAppContext) {
455    cx.add_model(|cx| {
456        let text = "fn a() {}";
457        let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
458
459        buffer.edit_with_autoindent([8..8], "\n\n", cx);
460        assert_eq!(buffer.text(), "fn a() {\n    \n}");
461
462        buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 4)], "b()\n", cx);
463        assert_eq!(buffer.text(), "fn a() {\n    b()\n    \n}");
464
465        buffer.edit_with_autoindent([Point::new(2, 4)..Point::new(2, 4)], ".c", cx);
466        assert_eq!(buffer.text(), "fn a() {\n    b()\n        .c\n}");
467
468        buffer
469    });
470}
471
472#[gpui::test]
473fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
474    cx.add_model(|cx| {
475        let text = "
476            fn a() {
477            c;
478            d;
479            }
480        "
481        .unindent();
482
483        let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
484
485        // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
486        // their indentation is not adjusted.
487        buffer.edit_with_autoindent([empty(Point::new(1, 1)), empty(Point::new(2, 1))], "()", cx);
488        assert_eq!(
489            buffer.text(),
490            "
491            fn a() {
492            c();
493            d();
494            }
495            "
496            .unindent()
497        );
498
499        // When appending new content after these lines, the indentation is based on the
500        // preceding lines' actual indentation.
501        buffer.edit_with_autoindent(
502            [empty(Point::new(1, 1)), empty(Point::new(2, 1))],
503            "\n.f\n.g",
504            cx,
505        );
506        assert_eq!(
507            buffer.text(),
508            "
509            fn a() {
510            c
511                .f
512                .g();
513            d
514                .f
515                .g();
516            }
517            "
518            .unindent()
519        );
520        buffer
521    });
522}
523
524#[gpui::test]
525fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
526    cx.add_model(|cx| {
527        let text = "
528            fn a() {}
529        "
530        .unindent();
531
532        let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
533
534        buffer.edit_with_autoindent([5..5], "\nb", cx);
535        assert_eq!(
536            buffer.text(),
537            "
538                fn a(
539                    b) {}
540            "
541            .unindent()
542        );
543
544        // The indentation suggestion changed because `@end` node (a close paren)
545        // is now at the beginning of the line.
546        buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 5)], "", cx);
547        assert_eq!(
548            buffer.text(),
549            "
550                fn a(
551                ) {}
552            "
553            .unindent()
554        );
555
556        buffer
557    });
558}
559
560#[gpui::test]
561fn test_serialization(cx: &mut gpui::MutableAppContext) {
562    let mut now = Instant::now();
563
564    let buffer1 = cx.add_model(|cx| {
565        let mut buffer = Buffer::new(0, "abc", cx);
566        buffer.edit([3..3], "D", cx);
567
568        now += Duration::from_secs(1);
569        buffer.start_transaction_at(now);
570        buffer.edit([4..4], "E", cx);
571        buffer.end_transaction_at(now, cx);
572        assert_eq!(buffer.text(), "abcDE");
573
574        buffer.undo(cx);
575        assert_eq!(buffer.text(), "abcD");
576
577        buffer.edit([4..4], "F", cx);
578        assert_eq!(buffer.text(), "abcDF");
579        buffer
580    });
581    assert_eq!(buffer1.read(cx).text(), "abcDF");
582
583    let message = buffer1.read(cx).to_proto();
584    let buffer2 = cx.add_model(|cx| Buffer::from_proto(1, message, None, cx).unwrap());
585    assert_eq!(buffer2.read(cx).text(), "abcDF");
586}
587
588#[gpui::test(iterations = 100)]
589fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
590    let min_peers = env::var("MIN_PEERS")
591        .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
592        .unwrap_or(1);
593    let max_peers = env::var("MAX_PEERS")
594        .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
595        .unwrap_or(5);
596    let operations = env::var("OPERATIONS")
597        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
598        .unwrap_or(10);
599
600    let base_text_len = rng.gen_range(0..10);
601    let base_text = RandomCharIter::new(&mut rng)
602        .take(base_text_len)
603        .collect::<String>();
604    let mut replica_ids = Vec::new();
605    let mut buffers = Vec::new();
606    let network = Rc::new(RefCell::new(Network::new(rng.clone())));
607
608    for i in 0..rng.gen_range(min_peers..=max_peers) {
609        let buffer = cx.add_model(|cx| {
610            let mut buffer = Buffer::new(i as ReplicaId, base_text.as_str(), cx);
611            buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
612            let network = network.clone();
613            cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
614                if let Event::Operation(op) = event {
615                    network
616                        .borrow_mut()
617                        .broadcast(buffer.replica_id(), vec![proto::serialize_operation(&op)]);
618                }
619            })
620            .detach();
621            buffer
622        });
623        buffers.push(buffer);
624        replica_ids.push(i as ReplicaId);
625        network.borrow_mut().add_peer(i as ReplicaId);
626        log::info!("Adding initial peer with replica id {}", i);
627    }
628
629    log::info!("initial text: {:?}", base_text);
630
631    let mut now = Instant::now();
632    let mut mutation_count = operations;
633    let mut next_diagnostic_id = 0;
634    let mut active_selections = BTreeMap::default();
635    loop {
636        let replica_index = rng.gen_range(0..replica_ids.len());
637        let replica_id = replica_ids[replica_index];
638        let buffer = &mut buffers[replica_index];
639        let mut new_buffer = None;
640        match rng.gen_range(0..100) {
641            0..=29 if mutation_count != 0 => {
642                buffer.update(cx, |buffer, cx| {
643                    buffer.start_transaction_at(now);
644                    buffer.randomly_edit(&mut rng, 5, cx);
645                    buffer.end_transaction_at(now, cx);
646                    log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
647                });
648                mutation_count -= 1;
649            }
650            30..=39 if mutation_count != 0 => {
651                buffer.update(cx, |buffer, cx| {
652                    let mut selections = Vec::new();
653                    for id in 0..rng.gen_range(1..=5) {
654                        let range = buffer.random_byte_range(0, &mut rng);
655                        selections.push(Selection {
656                            id,
657                            start: buffer.anchor_before(range.start),
658                            end: buffer.anchor_before(range.end),
659                            reversed: false,
660                            goal: SelectionGoal::None,
661                        });
662                    }
663                    let selections: Arc<[Selection<Anchor>]> = selections.into();
664                    log::info!(
665                        "peer {} setting active selections: {:?}",
666                        replica_id,
667                        selections
668                    );
669                    active_selections.insert(replica_id, selections.clone());
670                    buffer.set_active_selections(selections, cx);
671                });
672                mutation_count -= 1;
673            }
674            40..=49 if mutation_count != 0 && replica_id == 0 => {
675                let entry_count = rng.gen_range(1..=5);
676                buffer.update(cx, |buffer, cx| {
677                    let diagnostics = DiagnosticSet::new(
678                        (0..entry_count).map(|_| {
679                            let range = buffer.random_byte_range(0, &mut rng);
680                            let range = range.to_point_utf16(buffer);
681                            DiagnosticEntry {
682                                range,
683                                diagnostic: Diagnostic {
684                                    message: post_inc(&mut next_diagnostic_id).to_string(),
685                                    ..Default::default()
686                                },
687                            }
688                        }),
689                        buffer,
690                    );
691                    log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
692                    buffer.update_diagnostics(diagnostics, cx);
693                });
694                mutation_count -= 1;
695            }
696            50..=59 if replica_ids.len() < max_peers => {
697                let old_buffer = buffer.read(cx).to_proto();
698                let new_replica_id = replica_ids.len() as ReplicaId;
699                log::info!(
700                    "Adding new replica {} (replicating from {})",
701                    new_replica_id,
702                    replica_id
703                );
704                new_buffer = Some(cx.add_model(|cx| {
705                    let mut new_buffer =
706                        Buffer::from_proto(new_replica_id, old_buffer, None, cx).unwrap();
707                    new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
708                    let network = network.clone();
709                    cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
710                        if let Event::Operation(op) = event {
711                            network.borrow_mut().broadcast(
712                                buffer.replica_id(),
713                                vec![proto::serialize_operation(&op)],
714                            );
715                        }
716                    })
717                    .detach();
718                    new_buffer
719                }));
720                replica_ids.push(new_replica_id);
721                network.borrow_mut().replicate(replica_id, new_replica_id);
722            }
723            60..=69 if mutation_count != 0 => {
724                buffer.update(cx, |buffer, cx| {
725                    buffer.randomly_undo_redo(&mut rng, cx);
726                    log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
727                });
728                mutation_count -= 1;
729            }
730            _ if network.borrow().has_unreceived(replica_id) => {
731                let ops = network
732                    .borrow_mut()
733                    .receive(replica_id)
734                    .into_iter()
735                    .map(|op| proto::deserialize_operation(op).unwrap());
736                if ops.len() > 0 {
737                    log::info!(
738                        "peer {} applying {} ops from the network.",
739                        replica_id,
740                        ops.len()
741                    );
742                    buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
743                }
744            }
745            _ => {}
746        }
747
748        now += Duration::from_millis(rng.gen_range(0..=200));
749        buffers.extend(new_buffer);
750
751        for buffer in &buffers {
752            buffer.read(cx).check_invariants();
753        }
754
755        if mutation_count == 0 && network.borrow().is_idle() {
756            break;
757        }
758    }
759
760    let first_buffer = buffers[0].read(cx).snapshot();
761    for buffer in &buffers[1..] {
762        let buffer = buffer.read(cx).snapshot();
763        assert_eq!(
764            buffer.text(),
765            first_buffer.text(),
766            "Replica {} text != Replica 0 text",
767            buffer.replica_id()
768        );
769        assert_eq!(
770            buffer
771                .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
772                .collect::<Vec<_>>(),
773            first_buffer
774                .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
775                .collect::<Vec<_>>(),
776            "Replica {} diagnostics != Replica 0 diagnostics",
777            buffer.replica_id()
778        );
779    }
780
781    for buffer in &buffers {
782        let buffer = buffer.read(cx).snapshot();
783        let actual_remote_selections = buffer
784            .remote_selections_in_range(Anchor::min()..Anchor::max())
785            .map(|(replica_id, selections)| (replica_id, selections.collect::<Vec<_>>()))
786            .collect::<Vec<_>>();
787        let expected_remote_selections = active_selections
788            .iter()
789            .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
790            .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
791            .collect::<Vec<_>>();
792        assert_eq!(actual_remote_selections, expected_remote_selections);
793    }
794}
795
796#[test]
797fn test_contiguous_ranges() {
798    assert_eq!(
799        contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
800        &[1..4, 5..7, 9..13]
801    );
802
803    // Respects the `max_len` parameter
804    assert_eq!(
805        contiguous_ranges(
806            [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
807            3
808        )
809        .collect::<Vec<_>>(),
810        &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
811    );
812}
813
814impl Buffer {
815    pub fn enclosing_bracket_point_ranges<T: ToOffset>(
816        &self,
817        range: Range<T>,
818    ) -> Option<(Range<Point>, Range<Point>)> {
819        self.snapshot()
820            .enclosing_bracket_ranges(range)
821            .map(|(start, end)| {
822                let point_start = start.start.to_point(self)..start.end.to_point(self);
823                let point_end = end.start.to_point(self)..end.end.to_point(self);
824                (point_start, point_end)
825            })
826    }
827}
828
829fn rust_lang() -> Language {
830    Language::new(
831        LanguageConfig {
832            name: "Rust".into(),
833            path_suffixes: vec!["rs".to_string()],
834            language_server: None,
835            ..Default::default()
836        },
837        Some(tree_sitter_rust::language()),
838    )
839    .with_indents_query(
840        r#"
841        (call_expression) @indent
842        (field_expression) @indent
843        (_ "(" ")" @end) @indent
844        (_ "{" "}" @end) @indent
845        "#,
846    )
847    .unwrap()
848    .with_brackets_query(
849        r#"
850        ("{" @open "}" @close)
851        "#,
852    )
853    .unwrap()
854}
855
856fn empty(point: Point) -> Range<Point> {
857    point..point
858}