channel_buffer_tests.rs

  1use crate::{
  2    rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT},
  3    tests::TestServer,
  4};
  5use client::{Collaborator, UserId};
  6use collections::HashMap;
  7use futures::future;
  8use gpui::{BackgroundExecutor, Model, TestAppContext};
  9use rpc::{proto::PeerId, RECEIVE_TIMEOUT};
 10
 11#[gpui::test]
 12async fn test_core_channel_buffers(
 13    executor: BackgroundExecutor,
 14    cx_a: &mut TestAppContext,
 15    cx_b: &mut TestAppContext,
 16) {
 17    let mut server = TestServer::start(executor.clone()).await;
 18    let client_a = server.create_client(cx_a, "user_a").await;
 19    let client_b = server.create_client(cx_b, "user_b").await;
 20
 21    let channel_id = server
 22        .make_channel("zed", None, (&client_a, cx_a), &mut [(&client_b, cx_b)])
 23        .await;
 24
 25    // Client A joins the channel buffer
 26    let channel_buffer_a = client_a
 27        .channel_store()
 28        .update(cx_a, |store, cx| store.open_channel_buffer(channel_id, cx))
 29        .await
 30        .unwrap();
 31
 32    // Client A edits the buffer
 33    let buffer_a = channel_buffer_a.read_with(cx_a, |buffer, _| buffer.buffer());
 34    buffer_a.update(cx_a, |buffer, cx| {
 35        buffer.edit([(0..0, "hello world")], None, cx)
 36    });
 37    buffer_a.update(cx_a, |buffer, cx| {
 38        buffer.edit([(5..5, ", cruel")], None, cx)
 39    });
 40    buffer_a.update(cx_a, |buffer, cx| {
 41        buffer.edit([(0..5, "goodbye")], None, cx)
 42    });
 43    buffer_a.update(cx_a, |buffer, cx| buffer.undo(cx));
 44    assert_eq!(buffer_text(&buffer_a, cx_a), "hello, cruel world");
 45    executor.run_until_parked();
 46
 47    // Client B joins the channel buffer
 48    let channel_buffer_b = client_b
 49        .channel_store()
 50        .update(cx_b, |store, cx| store.open_channel_buffer(channel_id, cx))
 51        .await
 52        .unwrap();
 53    channel_buffer_b.read_with(cx_b, |buffer, _| {
 54        assert_collaborators(
 55            buffer.collaborators(),
 56            &[client_a.user_id(), client_b.user_id()],
 57        );
 58    });
 59
 60    // Client B sees the correct text, and then edits it
 61    let buffer_b = channel_buffer_b.read_with(cx_b, |buffer, _| buffer.buffer());
 62    assert_eq!(
 63        buffer_b.read_with(cx_b, |buffer, _| buffer.remote_id()),
 64        buffer_a.read_with(cx_a, |buffer, _| buffer.remote_id())
 65    );
 66    assert_eq!(buffer_text(&buffer_b, cx_b), "hello, cruel world");
 67    buffer_b.update(cx_b, |buffer, cx| {
 68        buffer.edit([(7..12, "beautiful")], None, cx)
 69    });
 70
 71    // Both A and B see the new edit
 72    executor.run_until_parked();
 73    assert_eq!(buffer_text(&buffer_a, cx_a), "hello, beautiful world");
 74    assert_eq!(buffer_text(&buffer_b, cx_b), "hello, beautiful world");
 75
 76    // Client A closes the channel buffer.
 77    cx_a.update(|_| drop(channel_buffer_a));
 78    executor.run_until_parked();
 79
 80    // Client B sees that client A is gone from the channel buffer.
 81    channel_buffer_b.read_with(cx_b, |buffer, _| {
 82        assert_collaborators(&buffer.collaborators(), &[client_b.user_id()]);
 83    });
 84
 85    // Client A rejoins the channel buffer
 86    let _channel_buffer_a = client_a
 87        .channel_store()
 88        .update(cx_a, |store, cx| store.open_channel_buffer(channel_id, cx))
 89        .await
 90        .unwrap();
 91    executor.run_until_parked();
 92
 93    // Sanity test, make sure we saw A rejoining
 94    channel_buffer_b.read_with(cx_b, |buffer, _| {
 95        assert_collaborators(
 96            &buffer.collaborators(),
 97            &[client_a.user_id(), client_b.user_id()],
 98        );
 99    });
100
101    // Client A loses connection.
102    server.forbid_connections();
103    server.disconnect_client(client_a.peer_id().unwrap());
104    executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
105
106    // Client B observes A disconnect
107    channel_buffer_b.read_with(cx_b, |buffer, _| {
108        assert_collaborators(&buffer.collaborators(), &[client_b.user_id()]);
109    });
110
111    // TODO:
112    // - Test synchronizing offline updates, what happens to A's channel buffer when A disconnects
113    // - Test interaction with channel deletion while buffer is open
114}
115
116// todo!("collab_ui")
117// #[gpui::test]
118// async fn test_channel_notes_participant_indices(
119//     executor: BackgroundExecutor,
120//     mut cx_a: &mut TestAppContext,
121//     mut cx_b: &mut TestAppContext,
122//     cx_c: &mut TestAppContext,
123// ) {
124//     let mut server = TestServer::start(&executor).await;
125//     let client_a = server.create_client(cx_a, "user_a").await;
126//     let client_b = server.create_client(cx_b, "user_b").await;
127//     let client_c = server.create_client(cx_c, "user_c").await;
128
129//     let active_call_a = cx_a.read(ActiveCall::global);
130//     let active_call_b = cx_b.read(ActiveCall::global);
131
132//     cx_a.update(editor::init);
133//     cx_b.update(editor::init);
134//     cx_c.update(editor::init);
135
136//     let channel_id = server
137//         .make_channel(
138//             "the-channel",
139//             None,
140//             (&client_a, cx_a),
141//             &mut [(&client_b, cx_b), (&client_c, cx_c)],
142//         )
143//         .await;
144
145//     client_a
146//         .fs()
147//         .insert_tree("/root", json!({"file.txt": "123"}))
148//         .await;
149//     let (project_a, worktree_id_a) = client_a.build_local_project("/root", cx_a).await;
150//     let project_b = client_b.build_empty_local_project(cx_b);
151//     let project_c = client_c.build_empty_local_project(cx_c);
152//     let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
153//     let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
154//     let workspace_c = client_c.build_workspace(&project_c, cx_c).root(cx_c);
155
156//     // Clients A, B, and C open the channel notes
157//     let channel_view_a = cx_a
158//         .update(|cx| ChannelView::open(channel_id, workspace_a.clone(), cx))
159//         .await
160//         .unwrap();
161//     let channel_view_b = cx_b
162//         .update(|cx| ChannelView::open(channel_id, workspace_b.clone(), cx))
163//         .await
164//         .unwrap();
165//     let channel_view_c = cx_c
166//         .update(|cx| ChannelView::open(channel_id, workspace_c.clone(), cx))
167//         .await
168//         .unwrap();
169
170//     // Clients A, B, and C all insert and select some text
171//     channel_view_a.update(cx_a, |notes, cx| {
172//         notes.editor.update(cx, |editor, cx| {
173//             editor.insert("a", cx);
174//             editor.change_selections(None, cx, |selections| {
175//                 selections.select_ranges(vec![0..1]);
176//             });
177//         });
178//     });
179//     executor.run_until_parked();
180//     channel_view_b.update(cx_b, |notes, cx| {
181//         notes.editor.update(cx, |editor, cx| {
182//             editor.move_down(&Default::default(), cx);
183//             editor.insert("b", cx);
184//             editor.change_selections(None, cx, |selections| {
185//                 selections.select_ranges(vec![1..2]);
186//             });
187//         });
188//     });
189//     executor.run_until_parked();
190//     channel_view_c.update(cx_c, |notes, cx| {
191//         notes.editor.update(cx, |editor, cx| {
192//             editor.move_down(&Default::default(), cx);
193//             editor.insert("c", cx);
194//             editor.change_selections(None, cx, |selections| {
195//                 selections.select_ranges(vec![2..3]);
196//             });
197//         });
198//     });
199
200//     // Client A sees clients B and C without assigned colors, because they aren't
201//     // in a call together.
202//     executor.run_until_parked();
203//     channel_view_a.update(cx_a, |notes, cx| {
204//         notes.editor.update(cx, |editor, cx| {
205//             assert_remote_selections(editor, &[(None, 1..2), (None, 2..3)], cx);
206//         });
207//     });
208
209//     // Clients A and B join the same call.
210//     for (call, cx) in [(&active_call_a, &mut cx_a), (&active_call_b, &mut cx_b)] {
211//         call.update(*cx, |call, cx| call.join_channel(channel_id, cx))
212//             .await
213//             .unwrap();
214//     }
215
216//     // Clients A and B see each other with two different assigned colors. Client C
217//     // still doesn't have a color.
218//     executor.run_until_parked();
219//     channel_view_a.update(cx_a, |notes, cx| {
220//         notes.editor.update(cx, |editor, cx| {
221//             assert_remote_selections(
222//                 editor,
223//                 &[(Some(ParticipantIndex(1)), 1..2), (None, 2..3)],
224//                 cx,
225//             );
226//         });
227//     });
228//     channel_view_b.update(cx_b, |notes, cx| {
229//         notes.editor.update(cx, |editor, cx| {
230//             assert_remote_selections(
231//                 editor,
232//                 &[(Some(ParticipantIndex(0)), 0..1), (None, 2..3)],
233//                 cx,
234//             );
235//         });
236//     });
237
238//     // Client A shares a project, and client B joins.
239//     let project_id = active_call_a
240//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
241//         .await
242//         .unwrap();
243//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
244//     let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
245
246//     // Clients A and B open the same file.
247//     let editor_a = workspace_a
248//         .update(cx_a, |workspace, cx| {
249//             workspace.open_path((worktree_id_a, "file.txt"), None, true, cx)
250//         })
251//         .await
252//         .unwrap()
253//         .downcast::<Editor>()
254//         .unwrap();
255//     let editor_b = workspace_b
256//         .update(cx_b, |workspace, cx| {
257//             workspace.open_path((worktree_id_a, "file.txt"), None, true, cx)
258//         })
259//         .await
260//         .unwrap()
261//         .downcast::<Editor>()
262//         .unwrap();
263
264//     editor_a.update(cx_a, |editor, cx| {
265//         editor.change_selections(None, cx, |selections| {
266//             selections.select_ranges(vec![0..1]);
267//         });
268//     });
269//     editor_b.update(cx_b, |editor, cx| {
270//         editor.change_selections(None, cx, |selections| {
271//             selections.select_ranges(vec![2..3]);
272//         });
273//     });
274//     executor.run_until_parked();
275
276//     // Clients A and B see each other with the same colors as in the channel notes.
277//     editor_a.update(cx_a, |editor, cx| {
278//         assert_remote_selections(editor, &[(Some(ParticipantIndex(1)), 2..3)], cx);
279//     });
280//     editor_b.update(cx_b, |editor, cx| {
281//         assert_remote_selections(editor, &[(Some(ParticipantIndex(0)), 0..1)], cx);
282//     });
283// }
284
285//todo!(editor)
286// #[track_caller]
287// fn assert_remote_selections(
288//     editor: &mut Editor,
289//     expected_selections: &[(Option<ParticipantIndex>, Range<usize>)],
290//     cx: &mut ViewContext<Editor>,
291// ) {
292//     let snapshot = editor.snapshot(cx);
293//     let range = Anchor::min()..Anchor::max();
294//     let remote_selections = snapshot
295//         .remote_selections_in_range(&range, editor.collaboration_hub().unwrap(), cx)
296//         .map(|s| {
297//             let start = s.selection.start.to_offset(&snapshot.buffer_snapshot);
298//             let end = s.selection.end.to_offset(&snapshot.buffer_snapshot);
299//             (s.participant_index, start..end)
300//         })
301//         .collect::<Vec<_>>();
302//     assert_eq!(
303//         remote_selections, expected_selections,
304//         "incorrect remote selections"
305//     );
306// }
307
308#[gpui::test]
309async fn test_multiple_handles_to_channel_buffer(
310    deterministic: BackgroundExecutor,
311    cx_a: &mut TestAppContext,
312) {
313    let mut server = TestServer::start(deterministic.clone()).await;
314    let client_a = server.create_client(cx_a, "user_a").await;
315
316    let channel_id = server
317        .make_channel("the-channel", None, (&client_a, cx_a), &mut [])
318        .await;
319
320    let channel_buffer_1 = client_a
321        .channel_store()
322        .update(cx_a, |store, cx| store.open_channel_buffer(channel_id, cx));
323    let channel_buffer_2 = client_a
324        .channel_store()
325        .update(cx_a, |store, cx| store.open_channel_buffer(channel_id, cx));
326    let channel_buffer_3 = client_a
327        .channel_store()
328        .update(cx_a, |store, cx| store.open_channel_buffer(channel_id, cx));
329
330    // All concurrent tasks for opening a channel buffer return the same model handle.
331    let (channel_buffer, channel_buffer_2, channel_buffer_3) =
332        future::try_join3(channel_buffer_1, channel_buffer_2, channel_buffer_3)
333            .await
334            .unwrap();
335    let channel_buffer_model_id = channel_buffer.entity_id();
336    assert_eq!(channel_buffer, channel_buffer_2);
337    assert_eq!(channel_buffer, channel_buffer_3);
338
339    channel_buffer.update(cx_a, |buffer, cx| {
340        buffer.buffer().update(cx, |buffer, cx| {
341            buffer.edit([(0..0, "hello")], None, cx);
342        })
343    });
344    deterministic.run_until_parked();
345
346    cx_a.update(|_| {
347        drop(channel_buffer);
348        drop(channel_buffer_2);
349        drop(channel_buffer_3);
350    });
351    deterministic.run_until_parked();
352
353    // The channel buffer can be reopened after dropping it.
354    let channel_buffer = client_a
355        .channel_store()
356        .update(cx_a, |store, cx| store.open_channel_buffer(channel_id, cx))
357        .await
358        .unwrap();
359    assert_ne!(channel_buffer.entity_id(), channel_buffer_model_id);
360    channel_buffer.update(cx_a, |buffer, cx| {
361        buffer.buffer().update(cx, |buffer, _| {
362            assert_eq!(buffer.text(), "hello");
363        })
364    });
365}
366
367#[gpui::test]
368async fn test_channel_buffer_disconnect(
369    deterministic: BackgroundExecutor,
370    cx_a: &mut TestAppContext,
371    cx_b: &mut TestAppContext,
372) {
373    let mut server = TestServer::start(deterministic.clone()).await;
374    let client_a = server.create_client(cx_a, "user_a").await;
375    let client_b = server.create_client(cx_b, "user_b").await;
376
377    let channel_id = server
378        .make_channel(
379            "the-channel",
380            None,
381            (&client_a, cx_a),
382            &mut [(&client_b, cx_b)],
383        )
384        .await;
385
386    let channel_buffer_a = client_a
387        .channel_store()
388        .update(cx_a, |store, cx| store.open_channel_buffer(channel_id, cx))
389        .await
390        .unwrap();
391
392    let channel_buffer_b = client_b
393        .channel_store()
394        .update(cx_b, |store, cx| store.open_channel_buffer(channel_id, cx))
395        .await
396        .unwrap();
397
398    server.forbid_connections();
399    server.disconnect_client(client_a.peer_id().unwrap());
400    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
401
402    channel_buffer_a.update(cx_a, |buffer, cx| {
403        assert_eq!(buffer.channel(cx).unwrap().name, "the-channel");
404        assert!(!buffer.is_connected());
405    });
406
407    deterministic.run_until_parked();
408
409    server.allow_connections();
410    deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
411
412    deterministic.run_until_parked();
413
414    client_a
415        .channel_store()
416        .update(cx_a, |channel_store, _| {
417            channel_store.remove_channel(channel_id)
418        })
419        .await
420        .unwrap();
421    deterministic.run_until_parked();
422
423    // Channel buffer observed the deletion
424    channel_buffer_b.update(cx_b, |buffer, cx| {
425        assert!(buffer.channel(cx).is_none());
426        assert!(!buffer.is_connected());
427    });
428}
429
430#[gpui::test]
431async fn test_rejoin_channel_buffer(
432    deterministic: BackgroundExecutor,
433    cx_a: &mut TestAppContext,
434    cx_b: &mut TestAppContext,
435) {
436    let mut server = TestServer::start(deterministic.clone()).await;
437    let client_a = server.create_client(cx_a, "user_a").await;
438    let client_b = server.create_client(cx_b, "user_b").await;
439
440    let channel_id = server
441        .make_channel(
442            "the-channel",
443            None,
444            (&client_a, cx_a),
445            &mut [(&client_b, cx_b)],
446        )
447        .await;
448
449    let channel_buffer_a = client_a
450        .channel_store()
451        .update(cx_a, |store, cx| store.open_channel_buffer(channel_id, cx))
452        .await
453        .unwrap();
454    let channel_buffer_b = client_b
455        .channel_store()
456        .update(cx_b, |store, cx| store.open_channel_buffer(channel_id, cx))
457        .await
458        .unwrap();
459
460    channel_buffer_a.update(cx_a, |buffer, cx| {
461        buffer.buffer().update(cx, |buffer, cx| {
462            buffer.edit([(0..0, "1")], None, cx);
463        })
464    });
465    deterministic.run_until_parked();
466
467    // Client A disconnects.
468    server.forbid_connections();
469    server.disconnect_client(client_a.peer_id().unwrap());
470
471    // Both clients make an edit.
472    channel_buffer_a.update(cx_a, |buffer, cx| {
473        buffer.buffer().update(cx, |buffer, cx| {
474            buffer.edit([(1..1, "2")], None, cx);
475        })
476    });
477    channel_buffer_b.update(cx_b, |buffer, cx| {
478        buffer.buffer().update(cx, |buffer, cx| {
479            buffer.edit([(0..0, "0")], None, cx);
480        })
481    });
482
483    // Both clients see their own edit.
484    deterministic.run_until_parked();
485    channel_buffer_a.read_with(cx_a, |buffer, cx| {
486        assert_eq!(buffer.buffer().read(cx).text(), "12");
487    });
488    channel_buffer_b.read_with(cx_b, |buffer, cx| {
489        assert_eq!(buffer.buffer().read(cx).text(), "01");
490    });
491
492    // Client A reconnects. Both clients see each other's edits, and see
493    // the same collaborators.
494    server.allow_connections();
495    deterministic.advance_clock(RECEIVE_TIMEOUT);
496    channel_buffer_a.read_with(cx_a, |buffer, cx| {
497        assert_eq!(buffer.buffer().read(cx).text(), "012");
498    });
499    channel_buffer_b.read_with(cx_b, |buffer, cx| {
500        assert_eq!(buffer.buffer().read(cx).text(), "012");
501    });
502
503    channel_buffer_a.read_with(cx_a, |buffer_a, _| {
504        channel_buffer_b.read_with(cx_b, |buffer_b, _| {
505            assert_eq!(buffer_a.collaborators(), buffer_b.collaborators());
506        });
507    });
508}
509
510#[gpui::test]
511async fn test_channel_buffers_and_server_restarts(
512    deterministic: BackgroundExecutor,
513    cx_a: &mut TestAppContext,
514    cx_b: &mut TestAppContext,
515    cx_c: &mut TestAppContext,
516) {
517    let mut server = TestServer::start(deterministic.clone()).await;
518    let client_a = server.create_client(cx_a, "user_a").await;
519    let client_b = server.create_client(cx_b, "user_b").await;
520    let client_c = server.create_client(cx_c, "user_c").await;
521
522    let channel_id = server
523        .make_channel(
524            "the-channel",
525            None,
526            (&client_a, cx_a),
527            &mut [(&client_b, cx_b), (&client_c, cx_c)],
528        )
529        .await;
530
531    let channel_buffer_a = client_a
532        .channel_store()
533        .update(cx_a, |store, cx| store.open_channel_buffer(channel_id, cx))
534        .await
535        .unwrap();
536    let channel_buffer_b = client_b
537        .channel_store()
538        .update(cx_b, |store, cx| store.open_channel_buffer(channel_id, cx))
539        .await
540        .unwrap();
541    let _channel_buffer_c = client_c
542        .channel_store()
543        .update(cx_c, |store, cx| store.open_channel_buffer(channel_id, cx))
544        .await
545        .unwrap();
546
547    channel_buffer_a.update(cx_a, |buffer, cx| {
548        buffer.buffer().update(cx, |buffer, cx| {
549            buffer.edit([(0..0, "1")], None, cx);
550        })
551    });
552    deterministic.run_until_parked();
553
554    // Client C can't reconnect.
555    client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
556
557    // Server stops.
558    server.reset().await;
559    deterministic.advance_clock(RECEIVE_TIMEOUT);
560
561    // While the server is down, both clients make an edit.
562    channel_buffer_a.update(cx_a, |buffer, cx| {
563        buffer.buffer().update(cx, |buffer, cx| {
564            buffer.edit([(1..1, "2")], None, cx);
565        })
566    });
567    channel_buffer_b.update(cx_b, |buffer, cx| {
568        buffer.buffer().update(cx, |buffer, cx| {
569            buffer.edit([(0..0, "0")], None, cx);
570        })
571    });
572
573    // Server restarts.
574    server.start().await.unwrap();
575    deterministic.advance_clock(CLEANUP_TIMEOUT);
576
577    // Clients reconnects. Clients A and B see each other's edits, and see
578    // that client C has disconnected.
579    channel_buffer_a.read_with(cx_a, |buffer, cx| {
580        assert_eq!(buffer.buffer().read(cx).text(), "012");
581    });
582    channel_buffer_b.read_with(cx_b, |buffer, cx| {
583        assert_eq!(buffer.buffer().read(cx).text(), "012");
584    });
585
586    channel_buffer_a.read_with(cx_a, |buffer_a, _| {
587        channel_buffer_b.read_with(cx_b, |buffer_b, _| {
588            assert_collaborators(
589                buffer_a.collaborators(),
590                &[client_a.user_id(), client_b.user_id()],
591            );
592            assert_eq!(buffer_a.collaborators(), buffer_b.collaborators());
593        });
594    });
595}
596
597//todo!(collab_ui)
598// #[gpui::test(iterations = 10)]
599// async fn test_following_to_channel_notes_without_a_shared_project(
600//     deterministic: BackgroundExecutor,
601//     mut cx_a: &mut TestAppContext,
602//     mut cx_b: &mut TestAppContext,
603//     mut cx_c: &mut TestAppContext,
604// ) {
605//     let mut server = TestServer::start(&deterministic).await;
606//     let client_a = server.create_client(cx_a, "user_a").await;
607//     let client_b = server.create_client(cx_b, "user_b").await;
608
609//     let client_c = server.create_client(cx_c, "user_c").await;
610
611//     cx_a.update(editor::init);
612//     cx_b.update(editor::init);
613//     cx_c.update(editor::init);
614//     cx_a.update(collab_ui::channel_view::init);
615//     cx_b.update(collab_ui::channel_view::init);
616//     cx_c.update(collab_ui::channel_view::init);
617
618//     let channel_1_id = server
619//         .make_channel(
620//             "channel-1",
621//             None,
622//             (&client_a, cx_a),
623//             &mut [(&client_b, cx_b), (&client_c, cx_c)],
624//         )
625//         .await;
626//     let channel_2_id = server
627//         .make_channel(
628//             "channel-2",
629//             None,
630//             (&client_a, cx_a),
631//             &mut [(&client_b, cx_b), (&client_c, cx_c)],
632//         )
633//         .await;
634
635//     // Clients A, B, and C join a channel.
636//     let active_call_a = cx_a.read(ActiveCall::global);
637//     let active_call_b = cx_b.read(ActiveCall::global);
638//     let active_call_c = cx_c.read(ActiveCall::global);
639//     for (call, cx) in [
640//         (&active_call_a, &mut cx_a),
641//         (&active_call_b, &mut cx_b),
642//         (&active_call_c, &mut cx_c),
643//     ] {
644//         call.update(*cx, |call, cx| call.join_channel(channel_1_id, cx))
645//             .await
646//             .unwrap();
647//     }
648//     deterministic.run_until_parked();
649
650//     // Clients A, B, and C all open their own unshared projects.
651//     client_a.fs().insert_tree("/a", json!({})).await;
652//     client_b.fs().insert_tree("/b", json!({})).await;
653//     client_c.fs().insert_tree("/c", json!({})).await;
654//     let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
655//     let (project_b, _) = client_b.build_local_project("/b", cx_b).await;
656//     let (project_c, _) = client_b.build_local_project("/c", cx_c).await;
657//     let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
658//     let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
659//     let _workspace_c = client_c.build_workspace(&project_c, cx_c).root(cx_c);
660
661//     active_call_a
662//         .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
663//         .await
664//         .unwrap();
665
666//     // Client A opens the notes for channel 1.
667//     let channel_view_1_a = cx_a
668//         .update(|cx| ChannelView::open(channel_1_id, workspace_a.clone(), cx))
669//         .await
670//         .unwrap();
671//     channel_view_1_a.update(cx_a, |notes, cx| {
672//         assert_eq!(notes.channel(cx).unwrap().name, "channel-1");
673//         notes.editor.update(cx, |editor, cx| {
674//             editor.insert("Hello from A.", cx);
675//             editor.change_selections(None, cx, |selections| {
676//                 selections.select_ranges(vec![3..4]);
677//             });
678//         });
679//     });
680
681//     // Client B follows client A.
682//     workspace_b
683//         .update(cx_b, |workspace, cx| {
684//             workspace.follow(client_a.peer_id().unwrap(), cx).unwrap()
685//         })
686//         .await
687//         .unwrap();
688
689//     // Client B is taken to the notes for channel 1, with the same
690//     // text selected as client A.
691//     deterministic.run_until_parked();
692//     let channel_view_1_b = workspace_b.read_with(cx_b, |workspace, cx| {
693//         assert_eq!(
694//             workspace.leader_for_pane(workspace.active_pane()),
695//             Some(client_a.peer_id().unwrap())
696//         );
697//         workspace
698//             .active_item(cx)
699//             .expect("no active item")
700//             .downcast::<ChannelView>()
701//             .expect("active item is not a channel view")
702//     });
703//     channel_view_1_b.read_with(cx_b, |notes, cx| {
704//         assert_eq!(notes.channel(cx).unwrap().name, "channel-1");
705//         let editor = notes.editor.read(cx);
706//         assert_eq!(editor.text(cx), "Hello from A.");
707//         assert_eq!(editor.selections.ranges::<usize>(cx), &[3..4]);
708//     });
709
710//     // Client A opens the notes for channel 2.
711//     let channel_view_2_a = cx_a
712//         .update(|cx| ChannelView::open(channel_2_id, workspace_a.clone(), cx))
713//         .await
714//         .unwrap();
715//     channel_view_2_a.read_with(cx_a, |notes, cx| {
716//         assert_eq!(notes.channel(cx).unwrap().name, "channel-2");
717//     });
718
719//     // Client B is taken to the notes for channel 2.
720//     deterministic.run_until_parked();
721//     let channel_view_2_b = workspace_b.read_with(cx_b, |workspace, cx| {
722//         assert_eq!(
723//             workspace.leader_for_pane(workspace.active_pane()),
724//             Some(client_a.peer_id().unwrap())
725//         );
726//         workspace
727//             .active_item(cx)
728//             .expect("no active item")
729//             .downcast::<ChannelView>()
730//             .expect("active item is not a channel view")
731//     });
732//     channel_view_2_b.read_with(cx_b, |notes, cx| {
733//         assert_eq!(notes.channel(cx).unwrap().name, "channel-2");
734//     });
735// }
736
737//todo!(collab_ui)
738// #[gpui::test]
739// async fn test_channel_buffer_changes(
740//     deterministic: BackgroundExecutor,
741//     cx_a: &mut TestAppContext,
742//     cx_b: &mut TestAppContext,
743// ) {
744//     let mut server = TestServer::start(&deterministic).await;
745//     let client_a = server.create_client(cx_a, "user_a").await;
746//     let client_b = server.create_client(cx_b, "user_b").await;
747
748//     let channel_id = server
749//         .make_channel(
750//             "the-channel",
751//             None,
752//             (&client_a, cx_a),
753//             &mut [(&client_b, cx_b)],
754//         )
755//         .await;
756
757//     let channel_buffer_a = client_a
758//         .channel_store()
759//         .update(cx_a, |store, cx| store.open_channel_buffer(channel_id, cx))
760//         .await
761//         .unwrap();
762
763//     // Client A makes an edit, and client B should see that the note has changed.
764//     channel_buffer_a.update(cx_a, |buffer, cx| {
765//         buffer.buffer().update(cx, |buffer, cx| {
766//             buffer.edit([(0..0, "1")], None, cx);
767//         })
768//     });
769//     deterministic.run_until_parked();
770
771//     let has_buffer_changed = cx_b.update(|cx| {
772//         client_b
773//             .channel_store()
774//             .read(cx)
775//             .has_channel_buffer_changed(channel_id)
776//             .unwrap()
777//     });
778//     assert!(has_buffer_changed);
779
780//     // Opening the buffer should clear the changed flag.
781//     let project_b = client_b.build_empty_local_project(cx_b);
782//     let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
783//     let channel_view_b = cx_b
784//         .update(|cx| ChannelView::open(channel_id, workspace_b.clone(), cx))
785//         .await
786//         .unwrap();
787//     deterministic.run_until_parked();
788
789//     let has_buffer_changed = cx_b.update(|cx| {
790//         client_b
791//             .channel_store()
792//             .read(cx)
793//             .has_channel_buffer_changed(channel_id)
794//             .unwrap()
795//     });
796//     assert!(!has_buffer_changed);
797
798//     // Editing the channel while the buffer is open should not show that the buffer has changed.
799//     channel_buffer_a.update(cx_a, |buffer, cx| {
800//         buffer.buffer().update(cx, |buffer, cx| {
801//             buffer.edit([(0..0, "2")], None, cx);
802//         })
803//     });
804//     deterministic.run_until_parked();
805
806//     let has_buffer_changed = cx_b.read(|cx| {
807//         client_b
808//             .channel_store()
809//             .read(cx)
810//             .has_channel_buffer_changed(channel_id)
811//             .unwrap()
812//     });
813//     assert!(!has_buffer_changed);
814
815//     deterministic.advance_clock(ACKNOWLEDGE_DEBOUNCE_INTERVAL);
816
817//     // Test that the server is tracking things correctly, and we retain our 'not changed'
818//     // state across a disconnect
819//     server.simulate_long_connection_interruption(client_b.peer_id().unwrap(), &deterministic);
820//     let has_buffer_changed = cx_b.read(|cx| {
821//         client_b
822//             .channel_store()
823//             .read(cx)
824//             .has_channel_buffer_changed(channel_id)
825//             .unwrap()
826//     });
827//     assert!(!has_buffer_changed);
828
829//     // Closing the buffer should re-enable change tracking
830//     cx_b.update(|cx| {
831//         workspace_b.update(cx, |workspace, cx| {
832//             workspace.close_all_items_and_panes(&Default::default(), cx)
833//         });
834
835//         drop(channel_view_b)
836//     });
837
838//     deterministic.run_until_parked();
839
840//     channel_buffer_a.update(cx_a, |buffer, cx| {
841//         buffer.buffer().update(cx, |buffer, cx| {
842//             buffer.edit([(0..0, "3")], None, cx);
843//         })
844//     });
845//     deterministic.run_until_parked();
846
847//     let has_buffer_changed = cx_b.read(|cx| {
848//         client_b
849//             .channel_store()
850//             .read(cx)
851//             .has_channel_buffer_changed(channel_id)
852//             .unwrap()
853//     });
854//     assert!(has_buffer_changed);
855// }
856
857#[track_caller]
858fn assert_collaborators(collaborators: &HashMap<PeerId, Collaborator>, ids: &[Option<UserId>]) {
859    let mut user_ids = collaborators
860        .values()
861        .map(|collaborator| collaborator.user_id)
862        .collect::<Vec<_>>();
863    user_ids.sort();
864    assert_eq!(
865        user_ids,
866        ids.into_iter().map(|id| id.unwrap()).collect::<Vec<_>>()
867    );
868}
869
870fn buffer_text(channel_buffer: &Model<language::Buffer>, cx: &mut TestAppContext) -> String {
871    channel_buffer.read_with(cx, |buffer, _| buffer.text())
872}