diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index e8198aea6b..f17ad95086 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -1328,21 +1328,24 @@ async fn test_project_reconnect( "/root-1", json!({ "dir1": { - "a.txt": "a-contents", - "b.txt": "b-contents", + "a.txt": "a", + "b.txt": "b", "subdir1": { - "c.txt": "c-contents", - "d.txt": "d-contents", - "e.txt": "e-contents", + "c.txt": "c", + "d.txt": "d", + "e.txt": "e", } }, "dir2": { - "x.txt": "x-contents", - "y.txt": "y-contents", - "z.txt": "z-contents", + "v.txt": "v", }, "dir3": { - "w.txt": "w-contents", + "w.txt": "w", + "x.txt": "x", + "y.txt": "y", + }, + "dir4": { + "z.txt": "z", }, }), ) @@ -1352,7 +1355,7 @@ async fn test_project_reconnect( .insert_tree( "/root-2", json!({ - "2.txt": "2-contents", + "2.txt": "2", }), ) .await; @@ -1361,7 +1364,7 @@ async fn test_project_reconnect( .insert_tree( "/root-3", json!({ - "3.txt": "3-contents", + "3.txt": "3", }), ) .await; @@ -1394,6 +1397,23 @@ async fn test_project_reconnect( assert!(worktree.as_local().unwrap().is_shared()); worktree.id() }); + let (worktree_a2, _) = project_a1 + .update(cx_a, |p, cx| { + p.find_or_create_local_worktree("/root-1/dir2", true, cx) + }) + .await + .unwrap(); + worktree_a2 + .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete()) + .await; + let worktree2_id = worktree_a2.read_with(cx_a, |tree, _| { + assert!(tree.as_local().unwrap().is_shared()); + tree.id() + }); + deterministic.run_until_parked(); + project_b1.read_with(cx_b, |project, cx| { + assert!(project.worktree_for_id(worktree2_id, cx).is_some()) + }); // Drop client A's connection. server.forbid_connections(); @@ -1436,17 +1456,22 @@ async fn test_project_reconnect( .await .unwrap(); - // While disconnected, add a worktree to client A's project. - let (worktree_a2, _) = project_a1 + // While disconnected, add and remove worktrees from client A's project. + project_a1 + .update(cx_a, |project, cx| { + project.remove_worktree(worktree2_id, cx) + }) + .await; + let (worktree_a3, _) = project_a1 .update(cx_a, |p, cx| { - p.find_or_create_local_worktree("/root-1/dir2", true, cx) + p.find_or_create_local_worktree("/root-1/dir3", true, cx) }) .await .unwrap(); - worktree_a2 + worktree_a3 .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete()) .await; - let worktree2_id = worktree_a2.read_with(cx_a, |tree, _| { + let worktree3_id = worktree_a3.read_with(cx_a, |tree, _| { assert!(tree.as_local().unwrap().is_shared()); tree.id() }); @@ -1455,6 +1480,11 @@ async fn test_project_reconnect( // While disconnected, close project 2 cx_a.update(|_| drop(project_a2)); + // While disconnected, mutate a buffer on both the host and the guest. + buffer_a1.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx)); + buffer_b1.update(cx_b, |buf, cx| buf.edit([(1..1, "Y")], None, cx)); + deterministic.run_until_parked(); + // Client A reconnects. Their project is re-shared, and client B re-joins it. server.allow_connections(); client_a @@ -1486,13 +1516,13 @@ async fn test_project_reconnect( ] ); assert_eq!( - worktree_a2 + worktree_a3 .read(cx) .snapshot() .paths() .map(|p| p.to_str().unwrap()) .collect::>(), - vec!["x.txt", "y.txt", "z.txt"] + vec!["w.txt", "x.txt", "y.txt"] ); }); project_b1.read_with(cx_b, |project, cx| { @@ -1520,16 +1550,17 @@ async fn test_project_reconnect( "subdir2/i.txt" ] ); + assert!(project.worktree_for_id(worktree2_id, cx).is_none()); assert_eq!( project - .worktree_for_id(worktree2_id, cx) + .worktree_for_id(worktree3_id, cx) .unwrap() .read(cx) .snapshot() .paths() .map(|p| p.to_str().unwrap()) .collect::>(), - vec!["x.txt", "y.txt", "z.txt"] + vec!["w.txt", "x.txt", "y.txt"] ); }); project_b2.read_with(cx_b, |project, _| assert!(project.is_read_only())); @@ -1552,22 +1583,22 @@ async fn test_project_reconnect( .unwrap(); // While client B is disconnected, add and remove worktrees from client A's project. - let (worktree_a3, _) = project_a1 + let (worktree_a4, _) = project_a1 .update(cx_a, |p, cx| { - p.find_or_create_local_worktree("/root-1/dir3", true, cx) + p.find_or_create_local_worktree("/root-1/dir4", true, cx) }) .await .unwrap(); - worktree_a3 + worktree_a4 .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete()) .await; - let worktree3_id = worktree_a3.read_with(cx_a, |tree, _| { + let worktree4_id = worktree_a4.read_with(cx_a, |tree, _| { assert!(tree.as_local().unwrap().is_shared()); tree.id() }); project_a1 .update(cx_a, |project, cx| { - project.remove_worktree(worktree2_id, cx) + project.remove_worktree(worktree3_id, cx) }) .await; deterministic.run_until_parked(); @@ -1610,14 +1641,14 @@ async fn test_project_reconnect( assert!(project.worktree_for_id(worktree2_id, cx).is_none()); assert_eq!( project - .worktree_for_id(worktree3_id, cx) + .worktree_for_id(worktree4_id, cx) .unwrap() .read(cx) .snapshot() .paths() .map(|p| p.to_str().unwrap()) .collect::>(), - vec!["w.txt"] + vec!["z.txt"] ); }); project_b3.read_with(cx_b, |project, _| assert!(project.is_read_only())); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 8dd4584ca2..17a38e9cfb 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4645,6 +4645,7 @@ impl Project { .remove(&old_peer_id) .ok_or_else(|| anyhow!("received UpdateProjectCollaborator for unknown peer"))?; this.collaborators.insert(new_peer_id, collaborator); + if let Some(buffers) = this.shared_buffers.remove(&old_peer_id) { this.shared_buffers.insert(new_peer_id, buffers); }