following_tests.rs

   1//todo!(workspace)
   2
   3// use crate::{rpc::RECONNECT_TIMEOUT, tests::TestServer};
   4// use call::ActiveCall;
   5// use collab_ui::notifications::project_shared_notification::ProjectSharedNotification;
   6// use editor::{Editor, ExcerptRange, MultiBuffer};
   7// use gpui::{BackgroundExecutor, TestAppContext, View};
   8// use live_kit_client::MacOSDisplay;
   9// use rpc::proto::PeerId;
  10// use serde_json::json;
  11// use std::borrow::Cow;
  12// use workspace::{
  13//     dock::{test::TestPanel, DockPosition},
  14//     item::{test::TestItem, ItemHandle as _},
  15//     shared_screen::SharedScreen,
  16//     SplitDirection, Workspace,
  17// };
  18
  19// #[gpui::test(iterations = 10)]
  20// async fn test_basic_following(
  21//     executor: BackgroundExecutor,
  22//     cx_a: &mut TestAppContext,
  23//     cx_b: &mut TestAppContext,
  24//     cx_c: &mut TestAppContext,
  25//     cx_d: &mut TestAppContext,
  26// ) {
  27//     let mut server = TestServer::start(&executor).await;
  28//     let client_a = server.create_client(cx_a, "user_a").await;
  29//     let client_b = server.create_client(cx_b, "user_b").await;
  30//     let client_c = server.create_client(cx_c, "user_c").await;
  31//     let client_d = server.create_client(cx_d, "user_d").await;
  32//     server
  33//         .create_room(&mut [
  34//             (&client_a, cx_a),
  35//             (&client_b, cx_b),
  36//             (&client_c, cx_c),
  37//             (&client_d, cx_d),
  38//         ])
  39//         .await;
  40//     let active_call_a = cx_a.read(ActiveCall::global);
  41//     let active_call_b = cx_b.read(ActiveCall::global);
  42
  43//     cx_a.update(editor::init);
  44//     cx_b.update(editor::init);
  45
  46//     client_a
  47//         .fs()
  48//         .insert_tree(
  49//             "/a",
  50//             json!({
  51//                 "1.txt": "one\none\none",
  52//                 "2.txt": "two\ntwo\ntwo",
  53//                 "3.txt": "three\nthree\nthree",
  54//             }),
  55//         )
  56//         .await;
  57//     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
  58//     active_call_a
  59//         .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
  60//         .await
  61//         .unwrap();
  62
  63//     let project_id = active_call_a
  64//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
  65//         .await
  66//         .unwrap();
  67//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
  68//     active_call_b
  69//         .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
  70//         .await
  71//         .unwrap();
  72
  73//     let window_a = client_a.build_workspace(&project_a, cx_a);
  74//     let workspace_a = window_a.root(cx_a);
  75//     let window_b = client_b.build_workspace(&project_b, cx_b);
  76//     let workspace_b = window_b.root(cx_b);
  77
  78//     // Client A opens some editors.
  79//     let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
  80//     let editor_a1 = workspace_a
  81//         .update(cx_a, |workspace, cx| {
  82//             workspace.open_path((worktree_id, "1.txt"), None, true, cx)
  83//         })
  84//         .await
  85//         .unwrap()
  86//         .downcast::<Editor>()
  87//         .unwrap();
  88//     let editor_a2 = workspace_a
  89//         .update(cx_a, |workspace, cx| {
  90//             workspace.open_path((worktree_id, "2.txt"), None, true, cx)
  91//         })
  92//         .await
  93//         .unwrap()
  94//         .downcast::<Editor>()
  95//         .unwrap();
  96
  97//     // Client B opens an editor.
  98//     let editor_b1 = workspace_b
  99//         .update(cx_b, |workspace, cx| {
 100//             workspace.open_path((worktree_id, "1.txt"), None, true, cx)
 101//         })
 102//         .await
 103//         .unwrap()
 104//         .downcast::<Editor>()
 105//         .unwrap();
 106
 107//     let peer_id_a = client_a.peer_id().unwrap();
 108//     let peer_id_b = client_b.peer_id().unwrap();
 109//     let peer_id_c = client_c.peer_id().unwrap();
 110//     let peer_id_d = client_d.peer_id().unwrap();
 111
 112//     // Client A updates their selections in those editors
 113//     editor_a1.update(cx_a, |editor, cx| {
 114//         editor.handle_input("a", cx);
 115//         editor.handle_input("b", cx);
 116//         editor.handle_input("c", cx);
 117//         editor.select_left(&Default::default(), cx);
 118//         assert_eq!(editor.selections.ranges(cx), vec![3..2]);
 119//     });
 120//     editor_a2.update(cx_a, |editor, cx| {
 121//         editor.handle_input("d", cx);
 122//         editor.handle_input("e", cx);
 123//         editor.select_left(&Default::default(), cx);
 124//         assert_eq!(editor.selections.ranges(cx), vec![2..1]);
 125//     });
 126
 127//     // When client B starts following client A, all visible view states are replicated to client B.
 128//     workspace_b
 129//         .update(cx_b, |workspace, cx| {
 130//             workspace.follow(peer_id_a, cx).unwrap()
 131//         })
 132//         .await
 133//         .unwrap();
 134
 135//     cx_c.foreground().run_until_parked();
 136//     let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
 137//         workspace
 138//             .active_item(cx)
 139//             .unwrap()
 140//             .downcast::<Editor>()
 141//             .unwrap()
 142//     });
 143//     assert_eq!(
 144//         cx_b.read(|cx| editor_b2.project_path(cx)),
 145//         Some((worktree_id, "2.txt").into())
 146//     );
 147//     assert_eq!(
 148//         editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
 149//         vec![2..1]
 150//     );
 151//     assert_eq!(
 152//         editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
 153//         vec![3..2]
 154//     );
 155
 156//     cx_c.foreground().run_until_parked();
 157//     let active_call_c = cx_c.read(ActiveCall::global);
 158//     let project_c = client_c.build_remote_project(project_id, cx_c).await;
 159//     let window_c = client_c.build_workspace(&project_c, cx_c);
 160//     let workspace_c = window_c.root(cx_c);
 161//     active_call_c
 162//         .update(cx_c, |call, cx| call.set_location(Some(&project_c), cx))
 163//         .await
 164//         .unwrap();
 165//     drop(project_c);
 166
 167//     // Client C also follows client A.
 168//     workspace_c
 169//         .update(cx_c, |workspace, cx| {
 170//             workspace.follow(peer_id_a, cx).unwrap()
 171//         })
 172//         .await
 173//         .unwrap();
 174
 175//     cx_d.foreground().run_until_parked();
 176//     let active_call_d = cx_d.read(ActiveCall::global);
 177//     let project_d = client_d.build_remote_project(project_id, cx_d).await;
 178//     let workspace_d = client_d.build_workspace(&project_d, cx_d).root(cx_d);
 179//     active_call_d
 180//         .update(cx_d, |call, cx| call.set_location(Some(&project_d), cx))
 181//         .await
 182//         .unwrap();
 183//     drop(project_d);
 184
 185//     // All clients see that clients B and C are following client A.
 186//     cx_c.foreground().run_until_parked();
 187//     for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
 188//         assert_eq!(
 189//             followers_by_leader(project_id, cx),
 190//             &[(peer_id_a, vec![peer_id_b, peer_id_c])],
 191//             "followers seen by {name}"
 192//         );
 193//     }
 194
 195//     // Client C unfollows client A.
 196//     workspace_c.update(cx_c, |workspace, cx| {
 197//         workspace.unfollow(&workspace.active_pane().clone(), cx);
 198//     });
 199
 200//     // All clients see that clients B is following client A.
 201//     cx_c.foreground().run_until_parked();
 202//     for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
 203//         assert_eq!(
 204//             followers_by_leader(project_id, cx),
 205//             &[(peer_id_a, vec![peer_id_b])],
 206//             "followers seen by {name}"
 207//         );
 208//     }
 209
 210//     // Client C re-follows client A.
 211//     workspace_c
 212//         .update(cx_c, |workspace, cx| {
 213//             workspace.follow(peer_id_a, cx).unwrap()
 214//         })
 215//         .await
 216//         .unwrap();
 217
 218//     // All clients see that clients B and C are following client A.
 219//     cx_c.foreground().run_until_parked();
 220//     for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
 221//         assert_eq!(
 222//             followers_by_leader(project_id, cx),
 223//             &[(peer_id_a, vec![peer_id_b, peer_id_c])],
 224//             "followers seen by {name}"
 225//         );
 226//     }
 227
 228//     // Client D follows client B, then switches to following client C.
 229//     workspace_d
 230//         .update(cx_d, |workspace, cx| {
 231//             workspace.follow(peer_id_b, cx).unwrap()
 232//         })
 233//         .await
 234//         .unwrap();
 235//     workspace_d
 236//         .update(cx_d, |workspace, cx| {
 237//             workspace.follow(peer_id_c, cx).unwrap()
 238//         })
 239//         .await
 240//         .unwrap();
 241
 242//     // All clients see that D is following C
 243//     cx_d.foreground().run_until_parked();
 244//     for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
 245//         assert_eq!(
 246//             followers_by_leader(project_id, cx),
 247//             &[
 248//                 (peer_id_a, vec![peer_id_b, peer_id_c]),
 249//                 (peer_id_c, vec![peer_id_d])
 250//             ],
 251//             "followers seen by {name}"
 252//         );
 253//     }
 254
 255//     // Client C closes the project.
 256//     window_c.remove(cx_c);
 257//     cx_c.drop_last(workspace_c);
 258
 259//     // Clients A and B see that client B is following A, and client C is not present in the followers.
 260//     cx_c.foreground().run_until_parked();
 261//     for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
 262//         assert_eq!(
 263//             followers_by_leader(project_id, cx),
 264//             &[(peer_id_a, vec![peer_id_b]),],
 265//             "followers seen by {name}"
 266//         );
 267//     }
 268
 269//     // When client A activates a different editor, client B does so as well.
 270//     workspace_a.update(cx_a, |workspace, cx| {
 271//         workspace.activate_item(&editor_a1, cx)
 272//     });
 273//     executor.run_until_parked();
 274//     workspace_b.read_with(cx_b, |workspace, cx| {
 275//         assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
 276//     });
 277
 278//     // When client A opens a multibuffer, client B does so as well.
 279//     let multibuffer_a = cx_a.add_model(|cx| {
 280//         let buffer_a1 = project_a.update(cx, |project, cx| {
 281//             project
 282//                 .get_open_buffer(&(worktree_id, "1.txt").into(), cx)
 283//                 .unwrap()
 284//         });
 285//         let buffer_a2 = project_a.update(cx, |project, cx| {
 286//             project
 287//                 .get_open_buffer(&(worktree_id, "2.txt").into(), cx)
 288//                 .unwrap()
 289//         });
 290//         let mut result = MultiBuffer::new(0);
 291//         result.push_excerpts(
 292//             buffer_a1,
 293//             [ExcerptRange {
 294//                 context: 0..3,
 295//                 primary: None,
 296//             }],
 297//             cx,
 298//         );
 299//         result.push_excerpts(
 300//             buffer_a2,
 301//             [ExcerptRange {
 302//                 context: 4..7,
 303//                 primary: None,
 304//             }],
 305//             cx,
 306//         );
 307//         result
 308//     });
 309//     let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
 310//         let editor =
 311//             cx.add_view(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), cx));
 312//         workspace.add_item(Box::new(editor.clone()), cx);
 313//         editor
 314//     });
 315//     executor.run_until_parked();
 316//     let multibuffer_editor_b = workspace_b.read_with(cx_b, |workspace, cx| {
 317//         workspace
 318//             .active_item(cx)
 319//             .unwrap()
 320//             .downcast::<Editor>()
 321//             .unwrap()
 322//     });
 323//     assert_eq!(
 324//         multibuffer_editor_a.read_with(cx_a, |editor, cx| editor.text(cx)),
 325//         multibuffer_editor_b.read_with(cx_b, |editor, cx| editor.text(cx)),
 326//     );
 327
 328//     // When client A navigates back and forth, client B does so as well.
 329//     workspace_a
 330//         .update(cx_a, |workspace, cx| {
 331//             workspace.go_back(workspace.active_pane().downgrade(), cx)
 332//         })
 333//         .await
 334//         .unwrap();
 335//     executor.run_until_parked();
 336//     workspace_b.read_with(cx_b, |workspace, cx| {
 337//         assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
 338//     });
 339
 340//     workspace_a
 341//         .update(cx_a, |workspace, cx| {
 342//             workspace.go_back(workspace.active_pane().downgrade(), cx)
 343//         })
 344//         .await
 345//         .unwrap();
 346//     executor.run_until_parked();
 347//     workspace_b.read_with(cx_b, |workspace, cx| {
 348//         assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b2.id());
 349//     });
 350
 351//     workspace_a
 352//         .update(cx_a, |workspace, cx| {
 353//             workspace.go_forward(workspace.active_pane().downgrade(), cx)
 354//         })
 355//         .await
 356//         .unwrap();
 357//     executor.run_until_parked();
 358//     workspace_b.read_with(cx_b, |workspace, cx| {
 359//         assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
 360//     });
 361
 362//     // Changes to client A's editor are reflected on client B.
 363//     editor_a1.update(cx_a, |editor, cx| {
 364//         editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
 365//     });
 366//     executor.run_until_parked();
 367//     editor_b1.read_with(cx_b, |editor, cx| {
 368//         assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
 369//     });
 370
 371//     editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
 372//     executor.run_until_parked();
 373//     editor_b1.read_with(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
 374
 375//     editor_a1.update(cx_a, |editor, cx| {
 376//         editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
 377//         editor.set_scroll_position(vec2f(0., 100.), cx);
 378//     });
 379//     executor.run_until_parked();
 380//     editor_b1.read_with(cx_b, |editor, cx| {
 381//         assert_eq!(editor.selections.ranges(cx), &[3..3]);
 382//     });
 383
 384//     // After unfollowing, client B stops receiving updates from client A.
 385//     workspace_b.update(cx_b, |workspace, cx| {
 386//         workspace.unfollow(&workspace.active_pane().clone(), cx)
 387//     });
 388//     workspace_a.update(cx_a, |workspace, cx| {
 389//         workspace.activate_item(&editor_a2, cx)
 390//     });
 391//     executor.run_until_parked();
 392//     assert_eq!(
 393//         workspace_b.read_with(cx_b, |workspace, cx| workspace
 394//             .active_item(cx)
 395//             .unwrap()
 396//             .id()),
 397//         editor_b1.id()
 398//     );
 399
 400//     // Client A starts following client B.
 401//     workspace_a
 402//         .update(cx_a, |workspace, cx| {
 403//             workspace.follow(peer_id_b, cx).unwrap()
 404//         })
 405//         .await
 406//         .unwrap();
 407//     assert_eq!(
 408//         workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
 409//         Some(peer_id_b)
 410//     );
 411//     assert_eq!(
 412//         workspace_a.read_with(cx_a, |workspace, cx| workspace
 413//             .active_item(cx)
 414//             .unwrap()
 415//             .id()),
 416//         editor_a1.id()
 417//     );
 418
 419//     // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
 420//     let display = MacOSDisplay::new();
 421//     active_call_b
 422//         .update(cx_b, |call, cx| call.set_location(None, cx))
 423//         .await
 424//         .unwrap();
 425//     active_call_b
 426//         .update(cx_b, |call, cx| {
 427//             call.room().unwrap().update(cx, |room, cx| {
 428//                 room.set_display_sources(vec![display.clone()]);
 429//                 room.share_screen(cx)
 430//             })
 431//         })
 432//         .await
 433//         .unwrap();
 434//     executor.run_until_parked();
 435//     let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
 436//         workspace
 437//             .active_item(cx)
 438//             .expect("no active item")
 439//             .downcast::<SharedScreen>()
 440//             .expect("active item isn't a shared screen")
 441//     });
 442
 443//     // Client B activates Zed again, which causes the previous editor to become focused again.
 444//     active_call_b
 445//         .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
 446//         .await
 447//         .unwrap();
 448//     executor.run_until_parked();
 449//     workspace_a.read_with(cx_a, |workspace, cx| {
 450//         assert_eq!(workspace.active_item(cx).unwrap().id(), editor_a1.id())
 451//     });
 452
 453//     // Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
 454//     workspace_b.update(cx_b, |workspace, cx| {
 455//         workspace.activate_item(&multibuffer_editor_b, cx)
 456//     });
 457//     executor.run_until_parked();
 458//     workspace_a.read_with(cx_a, |workspace, cx| {
 459//         assert_eq!(
 460//             workspace.active_item(cx).unwrap().id(),
 461//             multibuffer_editor_a.id()
 462//         )
 463//     });
 464
 465//     // Client B activates a panel, and the previously-opened screen-sharing item gets activated.
 466//     let panel = window_b.add_view(cx_b, |_| TestPanel::new(DockPosition::Left));
 467//     workspace_b.update(cx_b, |workspace, cx| {
 468//         workspace.add_panel(panel, cx);
 469//         workspace.toggle_panel_focus::<TestPanel>(cx);
 470//     });
 471//     executor.run_until_parked();
 472//     assert_eq!(
 473//         workspace_a.read_with(cx_a, |workspace, cx| workspace
 474//             .active_item(cx)
 475//             .unwrap()
 476//             .id()),
 477//         shared_screen.id()
 478//     );
 479
 480//     // Toggling the focus back to the pane causes client A to return to the multibuffer.
 481//     workspace_b.update(cx_b, |workspace, cx| {
 482//         workspace.toggle_panel_focus::<TestPanel>(cx);
 483//     });
 484//     executor.run_until_parked();
 485//     workspace_a.read_with(cx_a, |workspace, cx| {
 486//         assert_eq!(
 487//             workspace.active_item(cx).unwrap().id(),
 488//             multibuffer_editor_a.id()
 489//         )
 490//     });
 491
 492//     // Client B activates an item that doesn't implement following,
 493//     // so the previously-opened screen-sharing item gets activated.
 494//     let unfollowable_item = window_b.add_view(cx_b, |_| TestItem::new());
 495//     workspace_b.update(cx_b, |workspace, cx| {
 496//         workspace.active_pane().update(cx, |pane, cx| {
 497//             pane.add_item(Box::new(unfollowable_item), true, true, None, cx)
 498//         })
 499//     });
 500//     executor.run_until_parked();
 501//     assert_eq!(
 502//         workspace_a.read_with(cx_a, |workspace, cx| workspace
 503//             .active_item(cx)
 504//             .unwrap()
 505//             .id()),
 506//         shared_screen.id()
 507//     );
 508
 509//     // Following interrupts when client B disconnects.
 510//     client_b.disconnect(&cx_b.to_async());
 511//     executor.advance_clock(RECONNECT_TIMEOUT);
 512//     assert_eq!(
 513//         workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
 514//         None
 515//     );
 516// }
 517
 518// #[gpui::test]
 519// async fn test_following_tab_order(
 520//     executor: BackgroundExecutor,
 521//     cx_a: &mut TestAppContext,
 522//     cx_b: &mut TestAppContext,
 523// ) {
 524//     let mut server = TestServer::start(&executor).await;
 525//     let client_a = server.create_client(cx_a, "user_a").await;
 526//     let client_b = server.create_client(cx_b, "user_b").await;
 527//     server
 528//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 529//         .await;
 530//     let active_call_a = cx_a.read(ActiveCall::global);
 531//     let active_call_b = cx_b.read(ActiveCall::global);
 532
 533//     cx_a.update(editor::init);
 534//     cx_b.update(editor::init);
 535
 536//     client_a
 537//         .fs()
 538//         .insert_tree(
 539//             "/a",
 540//             json!({
 541//                 "1.txt": "one",
 542//                 "2.txt": "two",
 543//                 "3.txt": "three",
 544//             }),
 545//         )
 546//         .await;
 547//     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
 548//     active_call_a
 549//         .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
 550//         .await
 551//         .unwrap();
 552
 553//     let project_id = active_call_a
 554//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
 555//         .await
 556//         .unwrap();
 557//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
 558//     active_call_b
 559//         .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
 560//         .await
 561//         .unwrap();
 562
 563//     let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
 564//     let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
 565
 566//     let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
 567//     let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
 568
 569//     let client_b_id = project_a.read_with(cx_a, |project, _| {
 570//         project.collaborators().values().next().unwrap().peer_id
 571//     });
 572
 573//     //Open 1, 3 in that order on client A
 574//     workspace_a
 575//         .update(cx_a, |workspace, cx| {
 576//             workspace.open_path((worktree_id, "1.txt"), None, true, cx)
 577//         })
 578//         .await
 579//         .unwrap();
 580//     workspace_a
 581//         .update(cx_a, |workspace, cx| {
 582//             workspace.open_path((worktree_id, "3.txt"), None, true, cx)
 583//         })
 584//         .await
 585//         .unwrap();
 586
 587//     let pane_paths = |pane: &ViewHandle<workspace::Pane>, cx: &mut TestAppContext| {
 588//         pane.update(cx, |pane, cx| {
 589//             pane.items()
 590//                 .map(|item| {
 591//                     item.project_path(cx)
 592//                         .unwrap()
 593//                         .path
 594//                         .to_str()
 595//                         .unwrap()
 596//                         .to_owned()
 597//                 })
 598//                 .collect::<Vec<_>>()
 599//         })
 600//     };
 601
 602//     //Verify that the tabs opened in the order we expect
 603//     assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt"]);
 604
 605//     //Follow client B as client A
 606//     workspace_a
 607//         .update(cx_a, |workspace, cx| {
 608//             workspace.follow(client_b_id, cx).unwrap()
 609//         })
 610//         .await
 611//         .unwrap();
 612
 613//     //Open just 2 on client B
 614//     workspace_b
 615//         .update(cx_b, |workspace, cx| {
 616//             workspace.open_path((worktree_id, "2.txt"), None, true, cx)
 617//         })
 618//         .await
 619//         .unwrap();
 620//     executor.run_until_parked();
 621
 622//     // Verify that newly opened followed file is at the end
 623//     assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
 624
 625//     //Open just 1 on client B
 626//     workspace_b
 627//         .update(cx_b, |workspace, cx| {
 628//             workspace.open_path((worktree_id, "1.txt"), None, true, cx)
 629//         })
 630//         .await
 631//         .unwrap();
 632//     assert_eq!(&pane_paths(&pane_b, cx_b), &["2.txt", "1.txt"]);
 633//     executor.run_until_parked();
 634
 635//     // Verify that following into 1 did not reorder
 636//     assert_eq!(&pane_paths(&pane_a, cx_a), &["1.txt", "3.txt", "2.txt"]);
 637// }
 638
 639// #[gpui::test(iterations = 10)]
 640// async fn test_peers_following_each_other(
 641//     executor: BackgroundExecutor,
 642//     cx_a: &mut TestAppContext,
 643//     cx_b: &mut TestAppContext,
 644// ) {
 645//     let mut server = TestServer::start(&executor).await;
 646//     let client_a = server.create_client(cx_a, "user_a").await;
 647//     let client_b = server.create_client(cx_b, "user_b").await;
 648//     server
 649//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 650//         .await;
 651//     let active_call_a = cx_a.read(ActiveCall::global);
 652//     let active_call_b = cx_b.read(ActiveCall::global);
 653
 654//     cx_a.update(editor::init);
 655//     cx_b.update(editor::init);
 656
 657//     // Client A shares a project.
 658//     client_a
 659//         .fs()
 660//         .insert_tree(
 661//             "/a",
 662//             json!({
 663//                 "1.txt": "one",
 664//                 "2.txt": "two",
 665//                 "3.txt": "three",
 666//                 "4.txt": "four",
 667//             }),
 668//         )
 669//         .await;
 670//     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
 671//     active_call_a
 672//         .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
 673//         .await
 674//         .unwrap();
 675//     let project_id = active_call_a
 676//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
 677//         .await
 678//         .unwrap();
 679
 680//     // Client B joins the project.
 681//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
 682//     active_call_b
 683//         .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
 684//         .await
 685//         .unwrap();
 686
 687//     // Client A opens a file.
 688//     let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
 689//     workspace_a
 690//         .update(cx_a, |workspace, cx| {
 691//             workspace.open_path((worktree_id, "1.txt"), None, true, cx)
 692//         })
 693//         .await
 694//         .unwrap()
 695//         .downcast::<Editor>()
 696//         .unwrap();
 697
 698//     // Client B opens a different file.
 699//     let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
 700//     workspace_b
 701//         .update(cx_b, |workspace, cx| {
 702//             workspace.open_path((worktree_id, "2.txt"), None, true, cx)
 703//         })
 704//         .await
 705//         .unwrap()
 706//         .downcast::<Editor>()
 707//         .unwrap();
 708
 709//     // Clients A and B follow each other in split panes
 710//     workspace_a.update(cx_a, |workspace, cx| {
 711//         workspace.split_and_clone(workspace.active_pane().clone(), SplitDirection::Right, cx);
 712//     });
 713//     workspace_a
 714//         .update(cx_a, |workspace, cx| {
 715//             workspace.follow(client_b.peer_id().unwrap(), cx).unwrap()
 716//         })
 717//         .await
 718//         .unwrap();
 719//     workspace_b.update(cx_b, |workspace, cx| {
 720//         workspace.split_and_clone(workspace.active_pane().clone(), SplitDirection::Right, cx);
 721//     });
 722//     workspace_b
 723//         .update(cx_b, |workspace, cx| {
 724//             workspace.follow(client_a.peer_id().unwrap(), cx).unwrap()
 725//         })
 726//         .await
 727//         .unwrap();
 728
 729//     // Clients A and B return focus to the original files they had open
 730//     workspace_a.update(cx_a, |workspace, cx| workspace.activate_next_pane(cx));
 731//     workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
 732//     executor.run_until_parked();
 733
 734//     // Both clients see the other client's focused file in their right pane.
 735//     assert_eq!(
 736//         pane_summaries(&workspace_a, cx_a),
 737//         &[
 738//             PaneSummary {
 739//                 active: true,
 740//                 leader: None,
 741//                 items: vec![(true, "1.txt".into())]
 742//             },
 743//             PaneSummary {
 744//                 active: false,
 745//                 leader: client_b.peer_id(),
 746//                 items: vec![(false, "1.txt".into()), (true, "2.txt".into())]
 747//             },
 748//         ]
 749//     );
 750//     assert_eq!(
 751//         pane_summaries(&workspace_b, cx_b),
 752//         &[
 753//             PaneSummary {
 754//                 active: true,
 755//                 leader: None,
 756//                 items: vec![(true, "2.txt".into())]
 757//             },
 758//             PaneSummary {
 759//                 active: false,
 760//                 leader: client_a.peer_id(),
 761//                 items: vec![(false, "2.txt".into()), (true, "1.txt".into())]
 762//             },
 763//         ]
 764//     );
 765
 766//     // Clients A and B each open a new file.
 767//     workspace_a
 768//         .update(cx_a, |workspace, cx| {
 769//             workspace.open_path((worktree_id, "3.txt"), None, true, cx)
 770//         })
 771//         .await
 772//         .unwrap();
 773
 774//     workspace_b
 775//         .update(cx_b, |workspace, cx| {
 776//             workspace.open_path((worktree_id, "4.txt"), None, true, cx)
 777//         })
 778//         .await
 779//         .unwrap();
 780//     executor.run_until_parked();
 781
 782//     // Both client's see the other client open the new file, but keep their
 783//     // focus on their own active pane.
 784//     assert_eq!(
 785//         pane_summaries(&workspace_a, cx_a),
 786//         &[
 787//             PaneSummary {
 788//                 active: true,
 789//                 leader: None,
 790//                 items: vec![(false, "1.txt".into()), (true, "3.txt".into())]
 791//             },
 792//             PaneSummary {
 793//                 active: false,
 794//                 leader: client_b.peer_id(),
 795//                 items: vec![
 796//                     (false, "1.txt".into()),
 797//                     (false, "2.txt".into()),
 798//                     (true, "4.txt".into())
 799//                 ]
 800//             },
 801//         ]
 802//     );
 803//     assert_eq!(
 804//         pane_summaries(&workspace_b, cx_b),
 805//         &[
 806//             PaneSummary {
 807//                 active: true,
 808//                 leader: None,
 809//                 items: vec![(false, "2.txt".into()), (true, "4.txt".into())]
 810//             },
 811//             PaneSummary {
 812//                 active: false,
 813//                 leader: client_a.peer_id(),
 814//                 items: vec![
 815//                     (false, "2.txt".into()),
 816//                     (false, "1.txt".into()),
 817//                     (true, "3.txt".into())
 818//                 ]
 819//             },
 820//         ]
 821//     );
 822
 823//     // Client A focuses their right pane, in which they're following client B.
 824//     workspace_a.update(cx_a, |workspace, cx| workspace.activate_next_pane(cx));
 825//     executor.run_until_parked();
 826
 827//     // Client B sees that client A is now looking at the same file as them.
 828//     assert_eq!(
 829//         pane_summaries(&workspace_a, cx_a),
 830//         &[
 831//             PaneSummary {
 832//                 active: false,
 833//                 leader: None,
 834//                 items: vec![(false, "1.txt".into()), (true, "3.txt".into())]
 835//             },
 836//             PaneSummary {
 837//                 active: true,
 838//                 leader: client_b.peer_id(),
 839//                 items: vec![
 840//                     (false, "1.txt".into()),
 841//                     (false, "2.txt".into()),
 842//                     (true, "4.txt".into())
 843//                 ]
 844//             },
 845//         ]
 846//     );
 847//     assert_eq!(
 848//         pane_summaries(&workspace_b, cx_b),
 849//         &[
 850//             PaneSummary {
 851//                 active: true,
 852//                 leader: None,
 853//                 items: vec![(false, "2.txt".into()), (true, "4.txt".into())]
 854//             },
 855//             PaneSummary {
 856//                 active: false,
 857//                 leader: client_a.peer_id(),
 858//                 items: vec![
 859//                     (false, "2.txt".into()),
 860//                     (false, "1.txt".into()),
 861//                     (false, "3.txt".into()),
 862//                     (true, "4.txt".into())
 863//                 ]
 864//             },
 865//         ]
 866//     );
 867
 868//     // Client B focuses their right pane, in which they're following client A,
 869//     // who is following them.
 870//     workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
 871//     executor.run_until_parked();
 872
 873//     // Client A sees that client B is now looking at the same file as them.
 874//     assert_eq!(
 875//         pane_summaries(&workspace_b, cx_b),
 876//         &[
 877//             PaneSummary {
 878//                 active: false,
 879//                 leader: None,
 880//                 items: vec![(false, "2.txt".into()), (true, "4.txt".into())]
 881//             },
 882//             PaneSummary {
 883//                 active: true,
 884//                 leader: client_a.peer_id(),
 885//                 items: vec![
 886//                     (false, "2.txt".into()),
 887//                     (false, "1.txt".into()),
 888//                     (false, "3.txt".into()),
 889//                     (true, "4.txt".into())
 890//                 ]
 891//             },
 892//         ]
 893//     );
 894//     assert_eq!(
 895//         pane_summaries(&workspace_a, cx_a),
 896//         &[
 897//             PaneSummary {
 898//                 active: false,
 899//                 leader: None,
 900//                 items: vec![(false, "1.txt".into()), (true, "3.txt".into())]
 901//             },
 902//             PaneSummary {
 903//                 active: true,
 904//                 leader: client_b.peer_id(),
 905//                 items: vec![
 906//                     (false, "1.txt".into()),
 907//                     (false, "2.txt".into()),
 908//                     (true, "4.txt".into())
 909//                 ]
 910//             },
 911//         ]
 912//     );
 913
 914//     // Client B focuses a file that they previously followed A to, breaking
 915//     // the follow.
 916//     workspace_b.update(cx_b, |workspace, cx| {
 917//         workspace.active_pane().update(cx, |pane, cx| {
 918//             pane.activate_prev_item(true, cx);
 919//         });
 920//     });
 921//     executor.run_until_parked();
 922
 923//     // Both clients see that client B is looking at that previous file.
 924//     assert_eq!(
 925//         pane_summaries(&workspace_b, cx_b),
 926//         &[
 927//             PaneSummary {
 928//                 active: false,
 929//                 leader: None,
 930//                 items: vec![(false, "2.txt".into()), (true, "4.txt".into())]
 931//             },
 932//             PaneSummary {
 933//                 active: true,
 934//                 leader: None,
 935//                 items: vec![
 936//                     (false, "2.txt".into()),
 937//                     (false, "1.txt".into()),
 938//                     (true, "3.txt".into()),
 939//                     (false, "4.txt".into())
 940//                 ]
 941//             },
 942//         ]
 943//     );
 944//     assert_eq!(
 945//         pane_summaries(&workspace_a, cx_a),
 946//         &[
 947//             PaneSummary {
 948//                 active: false,
 949//                 leader: None,
 950//                 items: vec![(false, "1.txt".into()), (true, "3.txt".into())]
 951//             },
 952//             PaneSummary {
 953//                 active: true,
 954//                 leader: client_b.peer_id(),
 955//                 items: vec![
 956//                     (false, "1.txt".into()),
 957//                     (false, "2.txt".into()),
 958//                     (false, "4.txt".into()),
 959//                     (true, "3.txt".into()),
 960//                 ]
 961//             },
 962//         ]
 963//     );
 964
 965//     // Client B closes tabs, some of which were originally opened by client A,
 966//     // and some of which were originally opened by client B.
 967//     workspace_b.update(cx_b, |workspace, cx| {
 968//         workspace.active_pane().update(cx, |pane, cx| {
 969//             pane.close_inactive_items(&Default::default(), cx)
 970//                 .unwrap()
 971//                 .detach();
 972//         });
 973//     });
 974
 975//     executor.run_until_parked();
 976
 977//     // Both clients see that Client B is looking at the previous tab.
 978//     assert_eq!(
 979//         pane_summaries(&workspace_b, cx_b),
 980//         &[
 981//             PaneSummary {
 982//                 active: false,
 983//                 leader: None,
 984//                 items: vec![(false, "2.txt".into()), (true, "4.txt".into())]
 985//             },
 986//             PaneSummary {
 987//                 active: true,
 988//                 leader: None,
 989//                 items: vec![(true, "3.txt".into()),]
 990//             },
 991//         ]
 992//     );
 993//     assert_eq!(
 994//         pane_summaries(&workspace_a, cx_a),
 995//         &[
 996//             PaneSummary {
 997//                 active: false,
 998//                 leader: None,
 999//                 items: vec![(false, "1.txt".into()), (true, "3.txt".into())]
1000//             },
1001//             PaneSummary {
1002//                 active: true,
1003//                 leader: client_b.peer_id(),
1004//                 items: vec![
1005//                     (false, "1.txt".into()),
1006//                     (false, "2.txt".into()),
1007//                     (false, "4.txt".into()),
1008//                     (true, "3.txt".into()),
1009//                 ]
1010//             },
1011//         ]
1012//     );
1013
1014//     // Client B follows client A again.
1015//     workspace_b
1016//         .update(cx_b, |workspace, cx| {
1017//             workspace.follow(client_a.peer_id().unwrap(), cx).unwrap()
1018//         })
1019//         .await
1020//         .unwrap();
1021
1022//     // Client A cycles through some tabs.
1023//     workspace_a.update(cx_a, |workspace, cx| {
1024//         workspace.active_pane().update(cx, |pane, cx| {
1025//             pane.activate_prev_item(true, cx);
1026//         });
1027//     });
1028//     executor.run_until_parked();
1029
1030//     // Client B follows client A into those tabs.
1031//     assert_eq!(
1032//         pane_summaries(&workspace_a, cx_a),
1033//         &[
1034//             PaneSummary {
1035//                 active: false,
1036//                 leader: None,
1037//                 items: vec![(false, "1.txt".into()), (true, "3.txt".into())]
1038//             },
1039//             PaneSummary {
1040//                 active: true,
1041//                 leader: None,
1042//                 items: vec![
1043//                     (false, "1.txt".into()),
1044//                     (false, "2.txt".into()),
1045//                     (true, "4.txt".into()),
1046//                     (false, "3.txt".into()),
1047//                 ]
1048//             },
1049//         ]
1050//     );
1051//     assert_eq!(
1052//         pane_summaries(&workspace_b, cx_b),
1053//         &[
1054//             PaneSummary {
1055//                 active: false,
1056//                 leader: None,
1057//                 items: vec![(false, "2.txt".into()), (true, "4.txt".into())]
1058//             },
1059//             PaneSummary {
1060//                 active: true,
1061//                 leader: client_a.peer_id(),
1062//                 items: vec![(false, "3.txt".into()), (true, "4.txt".into())]
1063//             },
1064//         ]
1065//     );
1066
1067//     workspace_a.update(cx_a, |workspace, cx| {
1068//         workspace.active_pane().update(cx, |pane, cx| {
1069//             pane.activate_prev_item(true, cx);
1070//         });
1071//     });
1072//     executor.run_until_parked();
1073
1074//     assert_eq!(
1075//         pane_summaries(&workspace_a, cx_a),
1076//         &[
1077//             PaneSummary {
1078//                 active: false,
1079//                 leader: None,
1080//                 items: vec![(false, "1.txt".into()), (true, "3.txt".into())]
1081//             },
1082//             PaneSummary {
1083//                 active: true,
1084//                 leader: None,
1085//                 items: vec![
1086//                     (false, "1.txt".into()),
1087//                     (true, "2.txt".into()),
1088//                     (false, "4.txt".into()),
1089//                     (false, "3.txt".into()),
1090//                 ]
1091//             },
1092//         ]
1093//     );
1094//     assert_eq!(
1095//         pane_summaries(&workspace_b, cx_b),
1096//         &[
1097//             PaneSummary {
1098//                 active: false,
1099//                 leader: None,
1100//                 items: vec![(false, "2.txt".into()), (true, "4.txt".into())]
1101//             },
1102//             PaneSummary {
1103//                 active: true,
1104//                 leader: client_a.peer_id(),
1105//                 items: vec![
1106//                     (false, "3.txt".into()),
1107//                     (false, "4.txt".into()),
1108//                     (true, "2.txt".into())
1109//                 ]
1110//             },
1111//         ]
1112//     );
1113
1114//     workspace_a.update(cx_a, |workspace, cx| {
1115//         workspace.active_pane().update(cx, |pane, cx| {
1116//             pane.activate_prev_item(true, cx);
1117//         });
1118//     });
1119//     executor.run_until_parked();
1120
1121//     assert_eq!(
1122//         pane_summaries(&workspace_a, cx_a),
1123//         &[
1124//             PaneSummary {
1125//                 active: false,
1126//                 leader: None,
1127//                 items: vec![(false, "1.txt".into()), (true, "3.txt".into())]
1128//             },
1129//             PaneSummary {
1130//                 active: true,
1131//                 leader: None,
1132//                 items: vec![
1133//                     (true, "1.txt".into()),
1134//                     (false, "2.txt".into()),
1135//                     (false, "4.txt".into()),
1136//                     (false, "3.txt".into()),
1137//                 ]
1138//             },
1139//         ]
1140//     );
1141//     assert_eq!(
1142//         pane_summaries(&workspace_b, cx_b),
1143//         &[
1144//             PaneSummary {
1145//                 active: false,
1146//                 leader: None,
1147//                 items: vec![(false, "2.txt".into()), (true, "4.txt".into())]
1148//             },
1149//             PaneSummary {
1150//                 active: true,
1151//                 leader: client_a.peer_id(),
1152//                 items: vec![
1153//                     (false, "3.txt".into()),
1154//                     (false, "4.txt".into()),
1155//                     (false, "2.txt".into()),
1156//                     (true, "1.txt".into()),
1157//                 ]
1158//             },
1159//         ]
1160//     );
1161// }
1162
1163// #[gpui::test(iterations = 10)]
1164// async fn test_auto_unfollowing(
1165//     executor: BackgroundExecutor,
1166//     cx_a: &mut TestAppContext,
1167//     cx_b: &mut TestAppContext,
1168// ) {
1169//     // 2 clients connect to a server.
1170//     let mut server = TestServer::start(&executor).await;
1171//     let client_a = server.create_client(cx_a, "user_a").await;
1172//     let client_b = server.create_client(cx_b, "user_b").await;
1173//     server
1174//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
1175//         .await;
1176//     let active_call_a = cx_a.read(ActiveCall::global);
1177//     let active_call_b = cx_b.read(ActiveCall::global);
1178
1179//     cx_a.update(editor::init);
1180//     cx_b.update(editor::init);
1181
1182//     // Client A shares a project.
1183//     client_a
1184//         .fs()
1185//         .insert_tree(
1186//             "/a",
1187//             json!({
1188//                 "1.txt": "one",
1189//                 "2.txt": "two",
1190//                 "3.txt": "three",
1191//             }),
1192//         )
1193//         .await;
1194//     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
1195//     active_call_a
1196//         .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
1197//         .await
1198//         .unwrap();
1199
1200//     let project_id = active_call_a
1201//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1202//         .await
1203//         .unwrap();
1204//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
1205//     active_call_b
1206//         .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
1207//         .await
1208//         .unwrap();
1209
1210//     // Client A opens some editors.
1211//     let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
1212//     let _editor_a1 = workspace_a
1213//         .update(cx_a, |workspace, cx| {
1214//             workspace.open_path((worktree_id, "1.txt"), None, true, cx)
1215//         })
1216//         .await
1217//         .unwrap()
1218//         .downcast::<Editor>()
1219//         .unwrap();
1220
1221//     // Client B starts following client A.
1222//     let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
1223//     let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
1224//     let leader_id = project_b.read_with(cx_b, |project, _| {
1225//         project.collaborators().values().next().unwrap().peer_id
1226//     });
1227//     workspace_b
1228//         .update(cx_b, |workspace, cx| {
1229//             workspace.follow(leader_id, cx).unwrap()
1230//         })
1231//         .await
1232//         .unwrap();
1233//     assert_eq!(
1234//         workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
1235//         Some(leader_id)
1236//     );
1237//     let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
1238//         workspace
1239//             .active_item(cx)
1240//             .unwrap()
1241//             .downcast::<Editor>()
1242//             .unwrap()
1243//     });
1244
1245//     // When client B moves, it automatically stops following client A.
1246//     editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
1247//     assert_eq!(
1248//         workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
1249//         None
1250//     );
1251
1252//     workspace_b
1253//         .update(cx_b, |workspace, cx| {
1254//             workspace.follow(leader_id, cx).unwrap()
1255//         })
1256//         .await
1257//         .unwrap();
1258//     assert_eq!(
1259//         workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
1260//         Some(leader_id)
1261//     );
1262
1263//     // When client B edits, it automatically stops following client A.
1264//     editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
1265//     assert_eq!(
1266//         workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
1267//         None
1268//     );
1269
1270//     workspace_b
1271//         .update(cx_b, |workspace, cx| {
1272//             workspace.follow(leader_id, cx).unwrap()
1273//         })
1274//         .await
1275//         .unwrap();
1276//     assert_eq!(
1277//         workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
1278//         Some(leader_id)
1279//     );
1280
1281//     // When client B scrolls, it automatically stops following client A.
1282//     editor_b2.update(cx_b, |editor, cx| {
1283//         editor.set_scroll_position(vec2f(0., 3.), cx)
1284//     });
1285//     assert_eq!(
1286//         workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
1287//         None
1288//     );
1289
1290//     workspace_b
1291//         .update(cx_b, |workspace, cx| {
1292//             workspace.follow(leader_id, cx).unwrap()
1293//         })
1294//         .await
1295//         .unwrap();
1296//     assert_eq!(
1297//         workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
1298//         Some(leader_id)
1299//     );
1300
1301//     // When client B activates a different pane, it continues following client A in the original pane.
1302//     workspace_b.update(cx_b, |workspace, cx| {
1303//         workspace.split_and_clone(pane_b.clone(), SplitDirection::Right, cx)
1304//     });
1305//     assert_eq!(
1306//         workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
1307//         Some(leader_id)
1308//     );
1309
1310//     workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
1311//     assert_eq!(
1312//         workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
1313//         Some(leader_id)
1314//     );
1315
1316//     // When client B activates a different item in the original pane, it automatically stops following client A.
1317//     workspace_b
1318//         .update(cx_b, |workspace, cx| {
1319//             workspace.open_path((worktree_id, "2.txt"), None, true, cx)
1320//         })
1321//         .await
1322//         .unwrap();
1323//     assert_eq!(
1324//         workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
1325//         None
1326//     );
1327// }
1328
1329// #[gpui::test(iterations = 10)]
1330// async fn test_peers_simultaneously_following_each_other(
1331//     executor: BackgroundExecutor,
1332//     cx_a: &mut TestAppContext,
1333//     cx_b: &mut TestAppContext,
1334// ) {
1335//     let mut server = TestServer::start(&executor).await;
1336//     let client_a = server.create_client(cx_a, "user_a").await;
1337//     let client_b = server.create_client(cx_b, "user_b").await;
1338//     server
1339//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
1340//         .await;
1341//     let active_call_a = cx_a.read(ActiveCall::global);
1342
1343//     cx_a.update(editor::init);
1344//     cx_b.update(editor::init);
1345
1346//     client_a.fs().insert_tree("/a", json!({})).await;
1347//     let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
1348//     let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
1349//     let project_id = active_call_a
1350//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1351//         .await
1352//         .unwrap();
1353
1354//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
1355//     let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
1356
1357//     executor.run_until_parked();
1358//     let client_a_id = project_b.read_with(cx_b, |project, _| {
1359//         project.collaborators().values().next().unwrap().peer_id
1360//     });
1361//     let client_b_id = project_a.read_with(cx_a, |project, _| {
1362//         project.collaborators().values().next().unwrap().peer_id
1363//     });
1364
1365//     let a_follow_b = workspace_a.update(cx_a, |workspace, cx| {
1366//         workspace.follow(client_b_id, cx).unwrap()
1367//     });
1368//     let b_follow_a = workspace_b.update(cx_b, |workspace, cx| {
1369//         workspace.follow(client_a_id, cx).unwrap()
1370//     });
1371
1372//     futures::try_join!(a_follow_b, b_follow_a).unwrap();
1373//     workspace_a.read_with(cx_a, |workspace, _| {
1374//         assert_eq!(
1375//             workspace.leader_for_pane(workspace.active_pane()),
1376//             Some(client_b_id)
1377//         );
1378//     });
1379//     workspace_b.read_with(cx_b, |workspace, _| {
1380//         assert_eq!(
1381//             workspace.leader_for_pane(workspace.active_pane()),
1382//             Some(client_a_id)
1383//         );
1384//     });
1385// }
1386
1387// #[gpui::test(iterations = 10)]
1388// async fn test_following_across_workspaces(
1389//     executor: BackgroundExecutor,
1390//     cx_a: &mut TestAppContext,
1391//     cx_b: &mut TestAppContext,
1392// ) {
1393//     // a and b join a channel/call
1394//     // a shares project 1
1395//     // b shares project 2
1396//     //
1397//     // b follows a: causes project 2 to be joined, and b to follow a.
1398//     // b opens a different file in project 2, a follows b
1399//     // b opens a different file in project 1, a cannot follow b
1400//     // b shares the project, a joins the project and follows b
1401//     let mut server = TestServer::start(&executor).await;
1402//     let client_a = server.create_client(cx_a, "user_a").await;
1403//     let client_b = server.create_client(cx_b, "user_b").await;
1404//     cx_a.update(editor::init);
1405//     cx_b.update(editor::init);
1406
1407//     client_a
1408//         .fs()
1409//         .insert_tree(
1410//             "/a",
1411//             json!({
1412//                 "w.rs": "",
1413//                 "x.rs": "",
1414//             }),
1415//         )
1416//         .await;
1417
1418//     client_b
1419//         .fs()
1420//         .insert_tree(
1421//             "/b",
1422//             json!({
1423//                 "y.rs": "",
1424//                 "z.rs": "",
1425//             }),
1426//         )
1427//         .await;
1428
1429//     server
1430//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
1431//         .await;
1432//     let active_call_a = cx_a.read(ActiveCall::global);
1433//     let active_call_b = cx_b.read(ActiveCall::global);
1434
1435//     let (project_a, worktree_id_a) = client_a.build_local_project("/a", cx_a).await;
1436//     let (project_b, worktree_id_b) = client_b.build_local_project("/b", cx_b).await;
1437
1438//     let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
1439//     let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
1440
1441//     cx_a.update(|cx| collab_ui::init(&client_a.app_state, cx));
1442//     cx_b.update(|cx| collab_ui::init(&client_b.app_state, cx));
1443
1444//     active_call_a
1445//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
1446//         .await
1447//         .unwrap();
1448
1449//     active_call_a
1450//         .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
1451//         .await
1452//         .unwrap();
1453//     active_call_b
1454//         .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
1455//         .await
1456//         .unwrap();
1457
1458//     workspace_a
1459//         .update(cx_a, |workspace, cx| {
1460//             workspace.open_path((worktree_id_a, "w.rs"), None, true, cx)
1461//         })
1462//         .await
1463//         .unwrap();
1464
1465//     executor.run_until_parked();
1466//     assert_eq!(visible_push_notifications(cx_b).len(), 1);
1467
1468//     workspace_b.update(cx_b, |workspace, cx| {
1469//         workspace
1470//             .follow(client_a.peer_id().unwrap(), cx)
1471//             .unwrap()
1472//             .detach()
1473//     });
1474
1475//     executor.run_until_parked();
1476//     let workspace_b_project_a = cx_b
1477//         .windows()
1478//         .iter()
1479//         .max_by_key(|window| window.id())
1480//         .unwrap()
1481//         .downcast::<Workspace>()
1482//         .unwrap()
1483//         .root(cx_b);
1484
1485//     // assert that b is following a in project a in w.rs
1486//     workspace_b_project_a.update(cx_b, |workspace, cx| {
1487//         assert!(workspace.is_being_followed(client_a.peer_id().unwrap()));
1488//         assert_eq!(
1489//             client_a.peer_id(),
1490//             workspace.leader_for_pane(workspace.active_pane())
1491//         );
1492//         let item = workspace.active_item(cx).unwrap();
1493//         assert_eq!(item.tab_description(0, cx).unwrap(), Cow::Borrowed("w.rs"));
1494//     });
1495
1496//     // TODO: in app code, this would be done by the collab_ui.
1497//     active_call_b
1498//         .update(cx_b, |call, cx| {
1499//             let project = workspace_b_project_a.read(cx).project().clone();
1500//             call.set_location(Some(&project), cx)
1501//         })
1502//         .await
1503//         .unwrap();
1504
1505//     // assert that there are no share notifications open
1506//     assert_eq!(visible_push_notifications(cx_b).len(), 0);
1507
1508//     // b moves to x.rs in a's project, and a follows
1509//     workspace_b_project_a
1510//         .update(cx_b, |workspace, cx| {
1511//             workspace.open_path((worktree_id_a, "x.rs"), None, true, cx)
1512//         })
1513//         .await
1514//         .unwrap();
1515
1516//     executor.run_until_parked();
1517//     workspace_b_project_a.update(cx_b, |workspace, cx| {
1518//         let item = workspace.active_item(cx).unwrap();
1519//         assert_eq!(item.tab_description(0, cx).unwrap(), Cow::Borrowed("x.rs"));
1520//     });
1521
1522//     workspace_a.update(cx_a, |workspace, cx| {
1523//         workspace
1524//             .follow(client_b.peer_id().unwrap(), cx)
1525//             .unwrap()
1526//             .detach()
1527//     });
1528
1529//     executor.run_until_parked();
1530//     workspace_a.update(cx_a, |workspace, cx| {
1531//         assert!(workspace.is_being_followed(client_b.peer_id().unwrap()));
1532//         assert_eq!(
1533//             client_b.peer_id(),
1534//             workspace.leader_for_pane(workspace.active_pane())
1535//         );
1536//         let item = workspace.active_pane().read(cx).active_item().unwrap();
1537//         assert_eq!(item.tab_description(0, cx).unwrap(), Cow::Borrowed("x.rs"));
1538//     });
1539
1540//     // b moves to y.rs in b's project, a is still following but can't yet see
1541//     workspace_b
1542//         .update(cx_b, |workspace, cx| {
1543//             workspace.open_path((worktree_id_b, "y.rs"), None, true, cx)
1544//         })
1545//         .await
1546//         .unwrap();
1547
1548//     // TODO: in app code, this would be done by the collab_ui.
1549//     active_call_b
1550//         .update(cx_b, |call, cx| {
1551//             let project = workspace_b.read(cx).project().clone();
1552//             call.set_location(Some(&project), cx)
1553//         })
1554//         .await
1555//         .unwrap();
1556
1557//     let project_b_id = active_call_b
1558//         .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx))
1559//         .await
1560//         .unwrap();
1561
1562//     executor.run_until_parked();
1563//     assert_eq!(visible_push_notifications(cx_a).len(), 1);
1564//     cx_a.update(|cx| {
1565//         workspace::join_remote_project(
1566//             project_b_id,
1567//             client_b.user_id().unwrap(),
1568//             client_a.app_state.clone(),
1569//             cx,
1570//         )
1571//     })
1572//     .await
1573//     .unwrap();
1574
1575//     executor.run_until_parked();
1576
1577//     assert_eq!(visible_push_notifications(cx_a).len(), 0);
1578//     let workspace_a_project_b = cx_a
1579//         .windows()
1580//         .iter()
1581//         .max_by_key(|window| window.id())
1582//         .unwrap()
1583//         .downcast::<Workspace>()
1584//         .unwrap()
1585//         .root(cx_a);
1586
1587//     workspace_a_project_b.update(cx_a, |workspace, cx| {
1588//         assert_eq!(workspace.project().read(cx).remote_id(), Some(project_b_id));
1589//         assert!(workspace.is_being_followed(client_b.peer_id().unwrap()));
1590//         assert_eq!(
1591//             client_b.peer_id(),
1592//             workspace.leader_for_pane(workspace.active_pane())
1593//         );
1594//         let item = workspace.active_item(cx).unwrap();
1595//         assert_eq!(item.tab_description(0, cx).unwrap(), Cow::Borrowed("y.rs"));
1596//     });
1597// }
1598
1599// fn visible_push_notifications(
1600//     cx: &mut TestAppContext,
1601// ) -> Vec<gpui::ViewHandle<ProjectSharedNotification>> {
1602//     let mut ret = Vec::new();
1603//     for window in cx.windows() {
1604//         window.read_with(cx, |window| {
1605//             if let Some(handle) = window
1606//                 .root_view()
1607//                 .clone()
1608//                 .downcast::<ProjectSharedNotification>()
1609//             {
1610//                 ret.push(handle)
1611//             }
1612//         });
1613//     }
1614//     ret
1615// }
1616
1617// #[derive(Debug, PartialEq, Eq)]
1618// struct PaneSummary {
1619//     active: bool,
1620//     leader: Option<PeerId>,
1621//     items: Vec<(bool, String)>,
1622// }
1623
1624// fn followers_by_leader(project_id: u64, cx: &TestAppContext) -> Vec<(PeerId, Vec<PeerId>)> {
1625//     cx.read(|cx| {
1626//         let active_call = ActiveCall::global(cx).read(cx);
1627//         let peer_id = active_call.client().peer_id();
1628//         let room = active_call.room().unwrap().read(cx);
1629//         let mut result = room
1630//             .remote_participants()
1631//             .values()
1632//             .map(|participant| participant.peer_id)
1633//             .chain(peer_id)
1634//             .filter_map(|peer_id| {
1635//                 let followers = room.followers_for(peer_id, project_id);
1636//                 if followers.is_empty() {
1637//                     None
1638//                 } else {
1639//                     Some((peer_id, followers.to_vec()))
1640//                 }
1641//             })
1642//             .collect::<Vec<_>>();
1643//         result.sort_by_key(|e| e.0);
1644//         result
1645//     })
1646// }
1647
1648// fn pane_summaries(workspace: &ViewHandle<Workspace>, cx: &mut TestAppContext) -> Vec<PaneSummary> {
1649//     workspace.read_with(cx, |workspace, cx| {
1650//         let active_pane = workspace.active_pane();
1651//         workspace
1652//             .panes()
1653//             .iter()
1654//             .map(|pane| {
1655//                 let leader = workspace.leader_for_pane(pane);
1656//                 let active = pane == active_pane;
1657//                 let pane = pane.read(cx);
1658//                 let active_ix = pane.active_item_index();
1659//                 PaneSummary {
1660//                     active,
1661//                     leader,
1662//                     items: pane
1663//                         .items()
1664//                         .enumerate()
1665//                         .map(|(ix, item)| {
1666//                             (
1667//                                 ix == active_ix,
1668//                                 item.tab_description(0, cx)
1669//                                     .map_or(String::new(), |s| s.to_string()),
1670//                             )
1671//                         })
1672//                         .collect(),
1673//                 }
1674//             })
1675//             .collect()
1676//     })
1677// }