1use crate::{rpc::RECONNECT_TIMEOUT, tests::TestServer};
2
3use client::UserId;
4use gpui::{executor::Deterministic, ModelHandle, TestAppContext};
5use rpc::{proto, RECEIVE_TIMEOUT};
6use std::sync::Arc;
7
8#[gpui::test]
9async fn test_core_channel_buffers(
10 deterministic: Arc<Deterministic>,
11 cx_a: &mut TestAppContext,
12 cx_b: &mut TestAppContext,
13) {
14 deterministic.forbid_parking();
15 let mut server = TestServer::start(&deterministic).await;
16 let client_a = server.create_client(cx_a, "user_a").await;
17 let client_b = server.create_client(cx_b, "user_b").await;
18
19 let zed_id = server
20 .make_channel("zed", (&client_a, cx_a), &mut [(&client_b, cx_b)])
21 .await;
22
23 // Client A joins the channel buffer
24 let channel_buffer_a = client_a
25 .channel_store()
26 .update(cx_a, |channel, cx| channel.open_channel_buffer(zed_id, cx))
27 .await
28 .unwrap();
29
30 // Client A edits the buffer
31 let buffer_a = channel_buffer_a.read_with(cx_a, |buffer, _| buffer.buffer());
32
33 buffer_a.update(cx_a, |buffer, cx| {
34 buffer.edit([(0..0, "hello world")], None, cx)
35 });
36 buffer_a.update(cx_a, |buffer, cx| {
37 buffer.edit([(5..5, ", cruel")], None, cx)
38 });
39 buffer_a.update(cx_a, |buffer, cx| {
40 buffer.edit([(0..5, "goodbye")], None, cx)
41 });
42 buffer_a.update(cx_a, |buffer, cx| buffer.undo(cx));
43 deterministic.run_until_parked();
44
45 assert_eq!(buffer_text(&buffer_a, cx_a), "hello, cruel world");
46
47 // Client B joins the channel buffer
48 let channel_buffer_b = client_b
49 .channel_store()
50 .update(cx_b, |channel, cx| channel.open_channel_buffer(zed_id, cx))
51 .await
52 .unwrap();
53
54 channel_buffer_b.read_with(cx_b, |buffer, _| {
55 assert_collaborators(
56 buffer.collaborators(),
57 &[client_a.user_id(), client_b.user_id()],
58 );
59 });
60
61 // Client B sees the correct text, and then edits it
62 let buffer_b = channel_buffer_b.read_with(cx_b, |buffer, _| buffer.buffer());
63 assert_eq!(buffer_text(&buffer_b, cx_b), "hello, cruel world");
64 buffer_b.update(cx_b, |buffer, cx| {
65 buffer.edit([(7..12, "beautiful")], None, cx)
66 });
67
68 // Both A and B see the new edit
69 deterministic.run_until_parked();
70 assert_eq!(buffer_text(&buffer_a, cx_a), "hello, beautiful world");
71 assert_eq!(buffer_text(&buffer_b, cx_b), "hello, beautiful world");
72
73 // Client A closes the channel buffer.
74 cx_a.update(|_| drop(channel_buffer_a));
75 deterministic.run_until_parked();
76
77 // Client B sees that client A is gone from the channel buffer.
78 channel_buffer_b.read_with(cx_b, |buffer, _| {
79 assert_collaborators(&buffer.collaborators(), &[client_b.user_id()]);
80 });
81
82 // Client A rejoins the channel buffer
83 let _channel_buffer_a = client_a
84 .channel_store()
85 .update(cx_a, |channels, cx| channels.open_channel_buffer(zed_id, cx))
86 .await
87 .unwrap();
88 deterministic.run_until_parked();
89
90 // Sanity test, make sure we saw A rejoining
91 channel_buffer_b.read_with(cx_b, |buffer, _| {
92 assert_collaborators(
93 &buffer.collaborators(),
94 &[client_b.user_id(), client_a.user_id()],
95 );
96 });
97
98 // Client A loses connection.
99 server.forbid_connections();
100 server.disconnect_client(client_a.peer_id().unwrap());
101 deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
102
103 // Client B observes A disconnect
104 channel_buffer_b.read_with(cx_b, |buffer, _| {
105 assert_collaborators(&buffer.collaborators(), &[client_b.user_id()]);
106 });
107
108 // TODO:
109 // - Test synchronizing offline updates, what happens to A's channel buffer when A disconnects
110 // - Test interaction with channel deletion while buffer is open
111}
112
113#[track_caller]
114fn assert_collaborators(collaborators: &[proto::Collaborator], ids: &[Option<UserId>]) {
115 assert_eq!(
116 collaborators
117 .into_iter()
118 .map(|collaborator| collaborator.user_id)
119 .collect::<Vec<_>>(),
120 ids.into_iter().map(|id| id.unwrap()).collect::<Vec<_>>()
121 );
122}
123
124fn buffer_text(channel_buffer: &ModelHandle<language::Buffer>, cx: &mut TestAppContext) -> String {
125 channel_buffer.read_with(cx, |buffer, _| buffer.text())
126}