From 34bba303dc92c82f6e5e34a281ee6fe0907de429 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 15 Feb 2022 14:55:38 -0800 Subject: [PATCH] In random collaboration test, add failing assertion for worktree convergence --- crates/fuzzy/src/char_bag.rs | 2 +- crates/gpui/src/executor.rs | 7 +++++ crates/project/src/worktree.rs | 34 +++++++++++++++------ crates/server/src/rpc.rs | 52 +++++++++++++++++++++++++++++---- crates/sum_tree/src/sum_tree.rs | 8 +++++ 5 files changed, 88 insertions(+), 15 deletions(-) diff --git a/crates/fuzzy/src/char_bag.rs b/crates/fuzzy/src/char_bag.rs index c9aab0cd0b..135c5a768e 100644 --- a/crates/fuzzy/src/char_bag.rs +++ b/crates/fuzzy/src/char_bag.rs @@ -1,6 +1,6 @@ use std::iter::FromIterator; -#[derive(Copy, Clone, Debug, Default)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct CharBag(u64); impl CharBag { diff --git a/crates/gpui/src/executor.rs b/crates/gpui/src/executor.rs index e9e71e4b72..24cc60b996 100644 --- a/crates/gpui/src/executor.rs +++ b/crates/gpui/src/executor.rs @@ -370,6 +370,13 @@ impl Foreground { *any_value.downcast().unwrap() } + pub fn run_until_parked(&self) { + match self { + Self::Deterministic { executor, .. } => executor.run_until_parked(), + _ => panic!("this method can only be called on a deterministic executor"), + } + } + pub fn parking_forbidden(&self) -> bool { match self { Self::Deterministic { executor, .. } => executor.state.lock().forbid_parking, diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 748cdaa554..4a3e690c8b 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -87,7 +87,7 @@ pub struct RemoteWorktree { pending_updates: VecDeque, } -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct Snapshot { id: WorktreeId, root_name: String, @@ -1315,13 +1315,29 @@ impl fmt::Debug for LocalWorktree { impl fmt::Debug for Snapshot { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for entry in self.entries_by_path.cursor::<()>() { - for _ in entry.path.ancestors().skip(1) { - write!(f, " ")?; + struct EntriesById<'a>(&'a SumTree); + struct EntriesByPath<'a>(&'a SumTree); + + impl<'a> fmt::Debug for EntriesByPath<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map() + .entries(self.0.iter().map(|entry| (&entry.path, entry.id))) + .finish() } - writeln!(f, "{:?} (inode: {})", entry.path, entry.inode)?; } - Ok(()) + + impl<'a> fmt::Debug for EntriesById<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.0.iter()).finish() + } + } + + f.debug_struct("Snapshot") + .field("id", &self.id) + .field("root_name", &self.root_name) + .field("entries_by_path", &EntriesByPath(&self.entries_by_path)) + .field("entries_by_id", &EntriesById(&self.entries_by_id)) + .finish() } } @@ -1528,7 +1544,7 @@ impl File { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Entry { pub id: usize, pub kind: EntryKind, @@ -1539,7 +1555,7 @@ pub struct Entry { pub is_ignored: bool, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum EntryKind { PendingDir, Dir, @@ -1642,7 +1658,7 @@ impl sum_tree::Summary for EntrySummary { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] struct PathEntry { id: usize, path: Arc, diff --git a/crates/server/src/rpc.rs b/crates/server/src/rpc.rs index c88fba84ef..26a4832648 100644 --- a/crates/server/src/rpc.rs +++ b/crates/server/src/rpc.rs @@ -1085,6 +1085,7 @@ mod tests { github, AppState, Config, }; use ::rpc::Peer; + use collections::BTreeMap; use gpui::{executor, ModelHandle, TestAppContext}; use parking_lot::Mutex; use postage::{mpsc, watch}; @@ -3597,11 +3598,11 @@ mod tests { .unwrap(); clients.push(cx.foreground().spawn(host.simulate_host( - host_project, + host_project.clone(), operations.clone(), max_operations, rng.clone(), - host_cx, + host_cx.clone(), ))); while operations.get() < max_operations { @@ -3647,10 +3648,51 @@ mod tests { } let clients = futures::future::join_all(clients).await; + cx.foreground().run_until_parked(); + + let host_worktree_snapshots = host_project.read_with(&host_cx, |project, cx| { + project + .worktrees(cx) + .map(|worktree| { + let snapshot = worktree.read(cx).snapshot(); + (snapshot.id(), snapshot) + }) + .collect::>() + }); + for (ix, (client_a, cx_a)) in clients.iter().enumerate() { - for buffer_a in &client_a.buffers { - let buffer_id = buffer_a.read_with(cx_a, |buffer, _| buffer.remote_id()); - for (client_b, cx_b) in &clients[ix + 1..] { + let worktree_snapshots = + client_a + .project + .as_ref() + .unwrap() + .read_with(cx_a, |project, cx| { + project + .worktrees(cx) + .map(|worktree| { + let snapshot = worktree.read(cx).snapshot(); + (snapshot.id(), snapshot) + }) + .collect::>() + }); + + assert_eq!( + worktree_snapshots.keys().collect::>(), + host_worktree_snapshots.keys().collect::>(), + "guest {} has different worktrees than the host", + ix + ); + for (id, snapshot) in &host_worktree_snapshots { + assert_eq!( + worktree_snapshots[id], *snapshot, + "guest {} has different snapshot than the host for worktree {}", + ix, id + ); + } + + for (client_b, cx_b) in &clients[ix + 1..] { + for buffer_a in &client_a.buffers { + let buffer_id = buffer_a.read_with(cx_a, |buffer, _| buffer.remote_id()); if let Some(buffer_b) = client_b.buffers.iter().find(|buffer| { buffer.read_with(cx_b, |buffer, _| buffer.remote_id() == buffer_id) }) { diff --git a/crates/sum_tree/src/sum_tree.rs b/crates/sum_tree/src/sum_tree.rs index 67c056d858..ea21672b10 100644 --- a/crates/sum_tree/src/sum_tree.rs +++ b/crates/sum_tree/src/sum_tree.rs @@ -478,6 +478,14 @@ impl SumTree { } } +impl PartialEq for SumTree { + fn eq(&self, other: &Self) -> bool { + self.iter().eq(other.iter()) + } +} + +impl Eq for SumTree {} + impl SumTree { pub fn insert_or_replace(&mut self, item: T, cx: &::Context) -> bool { let mut replaced = false;