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