Verify removing worktrees while host is offline

This commit is contained in:
Antonio Scandurra 2022-12-21 11:47:01 +01:00
parent 59d7f06c57
commit ecd80c553c
2 changed files with 59 additions and 27 deletions

View file

@ -1328,21 +1328,24 @@ async fn test_project_reconnect(
"/root-1", "/root-1",
json!({ json!({
"dir1": { "dir1": {
"a.txt": "a-contents", "a.txt": "a",
"b.txt": "b-contents", "b.txt": "b",
"subdir1": { "subdir1": {
"c.txt": "c-contents", "c.txt": "c",
"d.txt": "d-contents", "d.txt": "d",
"e.txt": "e-contents", "e.txt": "e",
} }
}, },
"dir2": { "dir2": {
"x.txt": "x-contents", "v.txt": "v",
"y.txt": "y-contents",
"z.txt": "z-contents",
}, },
"dir3": { "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( .insert_tree(
"/root-2", "/root-2",
json!({ json!({
"2.txt": "2-contents", "2.txt": "2",
}), }),
) )
.await; .await;
@ -1361,7 +1364,7 @@ async fn test_project_reconnect(
.insert_tree( .insert_tree(
"/root-3", "/root-3",
json!({ json!({
"3.txt": "3-contents", "3.txt": "3",
}), }),
) )
.await; .await;
@ -1394,6 +1397,23 @@ async fn test_project_reconnect(
assert!(worktree.as_local().unwrap().is_shared()); assert!(worktree.as_local().unwrap().is_shared());
worktree.id() 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. // Drop client A's connection.
server.forbid_connections(); server.forbid_connections();
@ -1436,17 +1456,22 @@ async fn test_project_reconnect(
.await .await
.unwrap(); .unwrap();
// While disconnected, add a worktree to client A's project. // While disconnected, add and remove worktrees from client A's project.
let (worktree_a2, _) = project_a1 project_a1
.update(cx_a, |project, cx| {
project.remove_worktree(worktree2_id, cx)
})
.await;
let (worktree_a3, _) = project_a1
.update(cx_a, |p, cx| { .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 .await
.unwrap(); .unwrap();
worktree_a2 worktree_a3
.read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete()) .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
.await; .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()); assert!(tree.as_local().unwrap().is_shared());
tree.id() tree.id()
}); });
@ -1455,6 +1480,11 @@ async fn test_project_reconnect(
// While disconnected, close project 2 // While disconnected, close project 2
cx_a.update(|_| drop(project_a2)); 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. // Client A reconnects. Their project is re-shared, and client B re-joins it.
server.allow_connections(); server.allow_connections();
client_a client_a
@ -1486,13 +1516,13 @@ async fn test_project_reconnect(
] ]
); );
assert_eq!( assert_eq!(
worktree_a2 worktree_a3
.read(cx) .read(cx)
.snapshot() .snapshot()
.paths() .paths()
.map(|p| p.to_str().unwrap()) .map(|p| p.to_str().unwrap())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
vec!["x.txt", "y.txt", "z.txt"] vec!["w.txt", "x.txt", "y.txt"]
); );
}); });
project_b1.read_with(cx_b, |project, cx| { project_b1.read_with(cx_b, |project, cx| {
@ -1520,16 +1550,17 @@ async fn test_project_reconnect(
"subdir2/i.txt" "subdir2/i.txt"
] ]
); );
assert!(project.worktree_for_id(worktree2_id, cx).is_none());
assert_eq!( assert_eq!(
project project
.worktree_for_id(worktree2_id, cx) .worktree_for_id(worktree3_id, cx)
.unwrap() .unwrap()
.read(cx) .read(cx)
.snapshot() .snapshot()
.paths() .paths()
.map(|p| p.to_str().unwrap()) .map(|p| p.to_str().unwrap())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
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())); project_b2.read_with(cx_b, |project, _| assert!(project.is_read_only()));
@ -1552,22 +1583,22 @@ async fn test_project_reconnect(
.unwrap(); .unwrap();
// While client B is disconnected, add and remove worktrees from client A's project. // 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| { .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 .await
.unwrap(); .unwrap();
worktree_a3 worktree_a4
.read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete()) .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
.await; .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()); assert!(tree.as_local().unwrap().is_shared());
tree.id() tree.id()
}); });
project_a1 project_a1
.update(cx_a, |project, cx| { .update(cx_a, |project, cx| {
project.remove_worktree(worktree2_id, cx) project.remove_worktree(worktree3_id, cx)
}) })
.await; .await;
deterministic.run_until_parked(); deterministic.run_until_parked();
@ -1610,14 +1641,14 @@ async fn test_project_reconnect(
assert!(project.worktree_for_id(worktree2_id, cx).is_none()); assert!(project.worktree_for_id(worktree2_id, cx).is_none());
assert_eq!( assert_eq!(
project project
.worktree_for_id(worktree3_id, cx) .worktree_for_id(worktree4_id, cx)
.unwrap() .unwrap()
.read(cx) .read(cx)
.snapshot() .snapshot()
.paths() .paths()
.map(|p| p.to_str().unwrap()) .map(|p| p.to_str().unwrap())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
vec!["w.txt"] vec!["z.txt"]
); );
}); });
project_b3.read_with(cx_b, |project, _| assert!(project.is_read_only())); project_b3.read_with(cx_b, |project, _| assert!(project.is_read_only()));

View file

@ -4645,6 +4645,7 @@ impl Project {
.remove(&old_peer_id) .remove(&old_peer_id)
.ok_or_else(|| anyhow!("received UpdateProjectCollaborator for unknown peer"))?; .ok_or_else(|| anyhow!("received UpdateProjectCollaborator for unknown peer"))?;
this.collaborators.insert(new_peer_id, collaborator); this.collaborators.insert(new_peer_id, collaborator);
if let Some(buffers) = this.shared_buffers.remove(&old_peer_id) { if let Some(buffers) = this.shared_buffers.remove(&old_peer_id) {
this.shared_buffers.insert(new_peer_id, buffers); this.shared_buffers.insert(new_peer_id, buffers);
} }