collab: Extract tests into integration tests crate (#47668)

Piotr Osiewicz created

Reduces time needed to build collab tests from 48s to 38s.

Release Notes:

- N/A

Change summary

Cargo.lock                                                            |    1 
Cargo.toml                                                            |    1 
crates/collab/Cargo.toml                                              |    9 
crates/collab/src/auth.rs                                             |  183 
crates/collab/src/db.rs                                               |   25 
crates/collab/src/db/queries/channels.rs                              |   10 
crates/collab/src/db/queries/contributors.rs                          |    2 
crates/collab/src/db/tests.rs                                         |  217 
crates/collab/src/executor.rs                                         |    8 
crates/collab/src/lib.rs                                              |    5 
crates/collab/src/rpc.rs                                              |   16 
crates/collab/src/rpc/connection_pool.rs                              |    2 
crates/collab/src/tests.rs                                            |   51 
crates/collab/src/tests/debug_panel_tests.rs                          | 2449 
crates/collab/tests/integration/agent_sharing_tests.rs                |    2 
crates/collab/tests/integration/channel_buffer_tests.rs               |    6 
crates/collab/tests/integration/channel_guest_tests.rs                |    4 
crates/collab/tests/integration/channel_tests.rs                      |   10 
crates/collab/tests/integration/collab_tests.rs                       |  230 
crates/collab/tests/integration/db_tests.rs                           |  219 
crates/collab/tests/integration/db_tests/buffer_tests.rs              |    1 
crates/collab/tests/integration/db_tests/channel_tests.rs             |   14 
crates/collab/tests/integration/db_tests/db_tests.rs                  |   16 
crates/collab/tests/integration/db_tests/extension_tests.rs           |   12 
crates/collab/tests/integration/db_tests/migrations.rs                |    0 
crates/collab/tests/integration/editor_tests.rs                       |    3 
crates/collab/tests/integration/following_tests.rs                    |    4 
crates/collab/tests/integration/git_tests.rs                          |    2 
crates/collab/tests/integration/integration_tests.rs                  |    8 
crates/collab/tests/integration/notification_tests.rs                 |    2 
crates/collab/tests/integration/random_channel_buffer_tests.rs        |    7 
crates/collab/tests/integration/random_project_collaboration_tests.rs |    9 
crates/collab/tests/integration/randomized_test_helpers.rs            |    6 
crates/collab/tests/integration/remote_editing_collaboration_tests.rs |    2 
crates/collab/tests/integration/test_server.rs                        |   14 
35 files changed, 552 insertions(+), 2,998 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3293,6 +3293,7 @@ dependencies = [
  "chrono",
  "client",
  "clock",
+ "collab",
  "collab_ui",
  "collections",
  "command_palette_hooks",

Cargo.toml 🔗

@@ -278,6 +278,7 @@ clock = { path = "crates/clock" }
 cloud_api_client = { path = "crates/cloud_api_client" }
 cloud_api_types = { path = "crates/cloud_api_types" }
 cloud_llm_client = { path = "crates/cloud_llm_client" }
+collab = { path = "crates/collab" }
 collab_ui = { path = "crates/collab_ui" }
 collections = { path = "crates/collections", version = "0.1.0" }
 command_palette = { path = "crates/command_palette" }

crates/collab/Cargo.toml 🔗

@@ -17,6 +17,14 @@ name = "collab"
 sqlite = ["sea-orm/sqlx-sqlite", "sqlx/sqlite"]
 test-support = ["sqlite"]
 
+[lib]
+test = false
+
+[[test]]
+name = "collab_tests"
+required-features = ["test-support"]
+path = "tests/integration/collab_tests.rs"
+
 [dependencies]
 anyhow.workspace = true
 async-trait.workspace = true
@@ -81,6 +89,7 @@ buffer_diff.workspace = true
 call = { workspace = true, features = ["test-support"] }
 channel.workspace = true
 client = { workspace = true, features = ["test-support"] }
+collab = { workspace = true, features = ["test-support"] }
 collab_ui = { workspace = true, features = ["test-support"] }
 collections = { workspace = true, features = ["test-support"] }
 command_palette_hooks.workspace = true

crates/collab/src/auth.rs 🔗

@@ -108,13 +108,13 @@ pub async fn validate_header<B>(mut req: Request<B>, next: Next<B>) -> impl Into
     ))
 }
 
-const MAX_ACCESS_TOKENS_TO_STORE: usize = 8;
+pub const MAX_ACCESS_TOKENS_TO_STORE: usize = 8;
 
 #[derive(Serialize, Deserialize)]
-struct AccessTokenJson {
-    version: usize,
-    id: AccessTokenId,
-    token: String,
+pub struct AccessTokenJson {
+    pub version: usize,
+    pub id: AccessTokenId,
+    pub token: String,
 }
 
 /// Creates a new access token to identify the given user. before returning it, you should
@@ -224,176 +224,3 @@ pub async fn verify_access_token(
         },
     })
 }
-
-#[cfg(test)]
-mod test {
-    use rand::prelude::*;
-    use scrypt::password_hash::{PasswordHasher, SaltString};
-    use sea_orm::EntityTrait;
-
-    use super::*;
-    use crate::db::{NewUserParams, access_token};
-
-    #[gpui::test]
-    async fn test_verify_access_token(cx: &mut gpui::TestAppContext) {
-        let test_db = crate::db::TestDb::sqlite(cx.executor());
-        let db = test_db.db();
-
-        let user = db
-            .create_user(
-                "example@example.com",
-                None,
-                false,
-                NewUserParams {
-                    github_login: "example".into(),
-                    github_user_id: 1,
-                },
-            )
-            .await
-            .unwrap();
-
-        let token = create_access_token(db, user.user_id, None).await.unwrap();
-        assert!(matches!(
-            verify_access_token(&token, user.user_id, db).await.unwrap(),
-            VerifyAccessTokenResult {
-                is_valid: true,
-                impersonator_id: None,
-            }
-        ));
-
-        let old_token = create_previous_access_token(user.user_id, None, db)
-            .await
-            .unwrap();
-
-        let old_token_id = serde_json::from_str::<AccessTokenJson>(&old_token)
-            .unwrap()
-            .id;
-
-        let hash = db
-            .transaction(|tx| async move {
-                Ok(access_token::Entity::find_by_id(old_token_id)
-                    .one(&*tx)
-                    .await?)
-            })
-            .await
-            .unwrap()
-            .unwrap()
-            .hash;
-        assert!(hash.starts_with("$scrypt$"));
-
-        assert!(matches!(
-            verify_access_token(&old_token, user.user_id, db)
-                .await
-                .unwrap(),
-            VerifyAccessTokenResult {
-                is_valid: true,
-                impersonator_id: None,
-            }
-        ));
-
-        let hash = db
-            .transaction(|tx| async move {
-                Ok(access_token::Entity::find_by_id(old_token_id)
-                    .one(&*tx)
-                    .await?)
-            })
-            .await
-            .unwrap()
-            .unwrap()
-            .hash;
-        assert!(hash.starts_with("$sha256$"));
-
-        assert!(matches!(
-            verify_access_token(&old_token, user.user_id, db)
-                .await
-                .unwrap(),
-            VerifyAccessTokenResult {
-                is_valid: true,
-                impersonator_id: None,
-            }
-        ));
-
-        assert!(matches!(
-            verify_access_token(&token, user.user_id, db).await.unwrap(),
-            VerifyAccessTokenResult {
-                is_valid: true,
-                impersonator_id: None,
-            }
-        ));
-    }
-
-    async fn create_previous_access_token(
-        user_id: UserId,
-        impersonated_user_id: Option<UserId>,
-        db: &Database,
-    ) -> Result<String> {
-        let access_token = rpc::auth::random_token();
-        let access_token_hash = previous_hash_access_token(&access_token)?;
-        let id = db
-            .create_access_token(
-                user_id,
-                impersonated_user_id,
-                &access_token_hash,
-                MAX_ACCESS_TOKENS_TO_STORE,
-            )
-            .await?;
-        Ok(serde_json::to_string(&AccessTokenJson {
-            version: 1,
-            id,
-            token: access_token,
-        })?)
-    }
-
-    fn previous_hash_access_token(token: &str) -> Result<String> {
-        // Avoid slow hashing in debug mode.
-        let params = if cfg!(debug_assertions) {
-            scrypt::Params::new(1, 1, 1, scrypt::Params::RECOMMENDED_LEN).unwrap()
-        } else {
-            scrypt::Params::new(14, 8, 1, scrypt::Params::RECOMMENDED_LEN).unwrap()
-        };
-
-        Ok(Scrypt
-            .hash_password_customized(
-                token.as_bytes(),
-                None,
-                None,
-                params,
-                &SaltString::generate(PasswordHashRngCompat::new()),
-            )
-            .map_err(anyhow::Error::new)?
-            .to_string())
-    }
-
-    // TODO: remove once we password_hash v0.6 is released.
-    struct PasswordHashRngCompat(rand::rngs::ThreadRng);
-
-    impl PasswordHashRngCompat {
-        fn new() -> Self {
-            Self(rand::rng())
-        }
-    }
-
-    impl scrypt::password_hash::rand_core::RngCore for PasswordHashRngCompat {
-        fn next_u32(&mut self) -> u32 {
-            self.0.next_u32()
-        }
-
-        fn next_u64(&mut self) -> u64 {
-            self.0.next_u64()
-        }
-
-        fn fill_bytes(&mut self, dest: &mut [u8]) {
-            self.0.fill_bytes(dest);
-        }
-
-        fn try_fill_bytes(
-            &mut self,
-            dest: &mut [u8],
-        ) -> Result<(), scrypt::password_hash::rand_core::Error> {
-            self.fill_bytes(dest);
-            Ok(())
-        }
-    }
-
-    impl scrypt::password_hash::rand_core::CryptoRng for PasswordHashRngCompat {}
-}

crates/collab/src/db.rs 🔗

@@ -1,8 +1,6 @@
 mod ids;
-mod queries;
+pub mod queries;
 mod tables;
-#[cfg(test)]
-pub mod tests;
 
 use crate::{Error, Result};
 use anyhow::{Context as _, anyhow};
@@ -37,15 +35,12 @@ use tokio::sync::{Mutex, OwnedMutexGuard};
 use util::paths::PathStyle;
 use worktree_settings_file::LocalSettingsKind;
 
-#[cfg(test)]
-pub use tests::TestDb;
-
 pub use ids::*;
 pub use sea_orm::ConnectOptions;
 pub use tables::user::Model as User;
 pub use tables::*;
 
-#[cfg(test)]
+#[cfg(feature = "test-support")]
 pub struct DatabaseTestOptions {
     pub executor: gpui::BackgroundExecutor,
     pub runtime: tokio::runtime::Runtime,
@@ -55,14 +50,14 @@ pub struct DatabaseTestOptions {
 /// Database gives you a handle that lets you access the database.
 /// It handles pooling internally.
 pub struct Database {
-    options: ConnectOptions,
-    pool: DatabaseConnection,
+    pub options: ConnectOptions,
+    pub pool: DatabaseConnection,
     rooms: DashMap<RoomId, Arc<Mutex<()>>>,
     projects: DashMap<ProjectId, Arc<Mutex<()>>>,
     notification_kinds_by_id: HashMap<NotificationKindId, &'static str>,
     notification_kinds_by_name: HashMap<String, NotificationKindId>,
-    #[cfg(test)]
-    test_options: Option<DatabaseTestOptions>,
+    #[cfg(feature = "test-support")]
+    pub test_options: Option<DatabaseTestOptions>,
 }
 
 // The `Database` type has so many methods that its impl blocks are split into
@@ -78,7 +73,7 @@ impl Database {
             projects: DashMap::with_capacity(16384),
             notification_kinds_by_id: HashMap::default(),
             notification_kinds_by_name: HashMap::default(),
-            #[cfg(test)]
+            #[cfg(feature = "test-support")]
             test_options: None,
         })
     }
@@ -87,7 +82,7 @@ impl Database {
         &self.options
     }
 
-    #[cfg(test)]
+    #[cfg(feature = "test-support")]
     pub fn reset(&self) {
         self.rooms.clear();
         self.projects.clear();
@@ -248,7 +243,7 @@ impl Database {
     where
         F: Future<Output = Result<T>>,
     {
-        #[cfg(test)]
+        #[cfg(feature = "test-support")]
         {
             let test_options = self.test_options.as_ref().unwrap();
             test_options.executor.simulate_random_delay().await;
@@ -260,7 +255,7 @@ impl Database {
             test_options.runtime.block_on(future)
         }
 
-        #[cfg(not(test))]
+        #[cfg(not(feature = "test-support"))]
         {
             future.await
         }

crates/collab/src/db/queries/channels.rs 🔗

@@ -7,7 +7,7 @@ use rpc::{
 use sea_orm::{ActiveValue, DbBackend, TryGetableMany};
 
 impl Database {
-    #[cfg(test)]
+    #[cfg(feature = "test-support")]
     pub async fn all_channels(&self) -> Result<Vec<(ChannelId, String)>> {
         self.transaction(move |tx| async move {
             let mut channels = Vec::new();
@@ -21,12 +21,12 @@ impl Database {
         .await
     }
 
-    #[cfg(test)]
+    #[cfg(feature = "test-support")]
     pub async fn create_root_channel(&self, name: &str, creator_id: UserId) -> Result<ChannelId> {
         Ok(self.create_channel(name, None, creator_id).await?.0.id)
     }
 
-    #[cfg(test)]
+    #[cfg(feature = "test-support")]
     pub async fn create_sub_channel(
         &self,
         name: &str,
@@ -226,7 +226,7 @@ impl Database {
         .await
     }
 
-    #[cfg(test)]
+    #[cfg(feature = "test-support")]
     pub async fn set_channel_requires_zed_cla(
         &self,
         channel_id: ChannelId,
@@ -885,7 +885,7 @@ impl Database {
         .await
     }
 
-    pub(crate) async fn get_channel_internal(
+    pub async fn get_channel_internal(
         &self,
         channel_id: ChannelId,
         tx: &DatabaseTransaction,

crates/collab/src/db/queries/contributors.rs 🔗

@@ -2,7 +2,7 @@ use super::*;
 
 impl Database {
     /// Records that a given user has signed the CLA.
-    #[cfg(test)]
+    #[cfg(feature = "test-support")]
     pub async fn add_contributor(
         &self,
         github_login: &str,

crates/collab/src/db/tests.rs 🔗

@@ -1,218 +1 @@
-mod buffer_tests;
-mod channel_tests;
-mod db_tests;
-mod extension_tests;
-mod migrations;
 
-use std::sync::Arc;
-use std::sync::atomic::{AtomicI32, Ordering::SeqCst};
-use std::time::Duration;
-
-use gpui::BackgroundExecutor;
-use parking_lot::Mutex;
-use rand::prelude::*;
-use sea_orm::ConnectionTrait;
-use sqlx::migrate::MigrateDatabase;
-
-use self::migrations::run_database_migrations;
-
-use super::*;
-
-pub struct TestDb {
-    pub db: Option<Arc<Database>>,
-    pub connection: Option<sqlx::AnyConnection>,
-}
-
-impl TestDb {
-    pub fn sqlite(executor: BackgroundExecutor) -> Self {
-        let url = "sqlite::memory:";
-        let runtime = tokio::runtime::Builder::new_current_thread()
-            .enable_io()
-            .enable_time()
-            .build()
-            .unwrap();
-
-        let mut db = runtime.block_on(async {
-            let mut options = ConnectOptions::new(url);
-            options.max_connections(5);
-            let mut db = Database::new(options).await.unwrap();
-            let sql = include_str!(concat!(
-                env!("CARGO_MANIFEST_DIR"),
-                "/migrations.sqlite/20221109000000_test_schema.sql"
-            ));
-            db.pool
-                .execute(sea_orm::Statement::from_string(
-                    db.pool.get_database_backend(),
-                    sql,
-                ))
-                .await
-                .unwrap();
-            db.initialize_notification_kinds().await.unwrap();
-            db
-        });
-
-        db.test_options = Some(DatabaseTestOptions {
-            executor,
-            runtime,
-            query_failure_probability: parking_lot::Mutex::new(0.0),
-        });
-
-        Self {
-            db: Some(Arc::new(db)),
-            connection: None,
-        }
-    }
-
-    pub fn postgres(executor: BackgroundExecutor) -> Self {
-        static LOCK: Mutex<()> = Mutex::new(());
-
-        let _guard = LOCK.lock();
-        let mut rng = StdRng::from_os_rng();
-        let url = format!(
-            "postgres://postgres@localhost/zed-test-{}",
-            rng.random::<u128>()
-        );
-        let runtime = tokio::runtime::Builder::new_current_thread()
-            .enable_io()
-            .enable_time()
-            .build()
-            .unwrap();
-
-        let mut db = runtime.block_on(async {
-            sqlx::Postgres::create_database(&url)
-                .await
-                .expect("failed to create test db");
-            let mut options = ConnectOptions::new(url);
-            options
-                .max_connections(5)
-                .idle_timeout(Duration::from_secs(0));
-            let mut db = Database::new(options).await.unwrap();
-            let migrations_path = concat!(env!("CARGO_MANIFEST_DIR"), "/migrations");
-            run_database_migrations(db.options(), migrations_path)
-                .await
-                .unwrap();
-            db.initialize_notification_kinds().await.unwrap();
-            db
-        });
-
-        db.test_options = Some(DatabaseTestOptions {
-            executor,
-            runtime,
-            query_failure_probability: parking_lot::Mutex::new(0.0),
-        });
-
-        Self {
-            db: Some(Arc::new(db)),
-            connection: None,
-        }
-    }
-
-    pub fn db(&self) -> &Arc<Database> {
-        self.db.as_ref().unwrap()
-    }
-
-    pub fn set_query_failure_probability(&self, probability: f64) {
-        let database = self.db.as_ref().unwrap();
-        let test_options = database.test_options.as_ref().unwrap();
-        *test_options.query_failure_probability.lock() = probability;
-    }
-}
-
-#[macro_export]
-macro_rules! test_both_dbs {
-    ($test_name:ident, $postgres_test_name:ident, $sqlite_test_name:ident) => {
-        #[cfg(target_os = "macos")]
-        #[gpui::test]
-        async fn $postgres_test_name(cx: &mut gpui::TestAppContext) {
-            let test_db = $crate::db::TestDb::postgres(cx.executor().clone());
-            $test_name(test_db.db()).await;
-        }
-
-        #[gpui::test]
-        async fn $sqlite_test_name(cx: &mut gpui::TestAppContext) {
-            let test_db = $crate::db::TestDb::sqlite(cx.executor().clone());
-            $test_name(test_db.db()).await;
-        }
-    };
-}
-
-impl Drop for TestDb {
-    fn drop(&mut self) {
-        let db = self.db.take().unwrap();
-        if let sea_orm::DatabaseBackend::Postgres = db.pool.get_database_backend() {
-            db.test_options.as_ref().unwrap().runtime.block_on(async {
-                use util::ResultExt;
-                let query = "
-                        SELECT pg_terminate_backend(pg_stat_activity.pid)
-                        FROM pg_stat_activity
-                        WHERE
-                            pg_stat_activity.datname = current_database() AND
-                            pid <> pg_backend_pid();
-                    ";
-                db.pool
-                    .execute(sea_orm::Statement::from_string(
-                        db.pool.get_database_backend(),
-                        query,
-                    ))
-                    .await
-                    .log_err();
-                sqlx::Postgres::drop_database(db.options.get_url())
-                    .await
-                    .log_err();
-            })
-        }
-    }
-}
-
-#[track_caller]
-fn assert_channel_tree_matches(actual: Vec<Channel>, expected: Vec<Channel>) {
-    let expected_channels = expected.into_iter().collect::<HashSet<_>>();
-    let actual_channels = actual.into_iter().collect::<HashSet<_>>();
-    pretty_assertions::assert_eq!(expected_channels, actual_channels);
-}
-
-fn channel_tree(channels: &[(ChannelId, &[ChannelId], &'static str)]) -> Vec<Channel> {
-    use std::collections::HashMap;
-
-    let mut result = Vec::new();
-    let mut order_by_parent: HashMap<Vec<ChannelId>, i32> = HashMap::new();
-
-    for (id, parent_path, name) in channels {
-        let parent_key = parent_path.to_vec();
-        let order = if parent_key.is_empty() {
-            1
-        } else {
-            *order_by_parent
-                .entry(parent_key.clone())
-                .and_modify(|e| *e += 1)
-                .or_insert(1)
-        };
-
-        result.push(Channel {
-            id: *id,
-            name: (*name).to_owned(),
-            visibility: ChannelVisibility::Members,
-            parent_path: parent_key,
-            channel_order: order,
-        });
-    }
-
-    result
-}
-
-static GITHUB_USER_ID: AtomicI32 = AtomicI32::new(5);
-
-async fn new_test_user(db: &Arc<Database>, email: &str) -> UserId {
-    db.create_user(
-        email,
-        None,
-        false,
-        NewUserParams {
-            github_login: email[0..email.find('@').unwrap()].to_string(),
-            github_user_id: GITHUB_USER_ID.fetch_add(1, SeqCst),
-        },
-    )
-    .await
-    .unwrap()
-    .user_id
-}

crates/collab/src/executor.rs 🔗

@@ -1,12 +1,12 @@
 use std::{future::Future, time::Duration};
 
-#[cfg(test)]
+#[cfg(feature = "test-support")]
 use gpui::BackgroundExecutor;
 
 #[derive(Clone)]
 pub enum Executor {
     Production,
-    #[cfg(test)]
+    #[cfg(feature = "test-support")]
     Deterministic(BackgroundExecutor),
 }
 
@@ -19,7 +19,7 @@ impl Executor {
             Executor::Production => {
                 tokio::spawn(future);
             }
-            #[cfg(test)]
+            #[cfg(feature = "test-support")]
             Executor::Deterministic(background) => {
                 background.spawn(future).detach();
             }
@@ -31,7 +31,7 @@ impl Executor {
         async move {
             match this {
                 Executor::Production => tokio::time::sleep(duration).await,
-                #[cfg(test)]
+                #[cfg(feature = "test-support")]
                 Executor::Deterministic(background) => background.timer(duration).await,
             }
         }

crates/collab/src/lib.rs 🔗

@@ -6,9 +6,6 @@ pub mod executor;
 pub mod rpc;
 pub mod seed;
 
-#[cfg(test)]
-mod tests;
-
 use anyhow::Context as _;
 use aws_config::{BehaviorVersion, Region};
 use axum::{
@@ -169,7 +166,7 @@ impl Config {
         }
     }
 
-    #[cfg(test)]
+    #[cfg(feature = "test-support")]
     pub fn test() -> Self {
         Self {
             http_port: 0,

crates/collab/src/rpc.rs 🔗

@@ -204,16 +204,16 @@ struct Session {
 
 impl Session {
     async fn db(&self) -> tokio::sync::MutexGuard<'_, DbHandle> {
-        #[cfg(test)]
+        #[cfg(feature = "test-support")]
         tokio::task::yield_now().await;
         let guard = self.db.lock().await;
-        #[cfg(test)]
+        #[cfg(feature = "test-support")]
         tokio::task::yield_now().await;
         guard
     }
 
     async fn connection_pool(&self) -> ConnectionPoolGuard<'_> {
-        #[cfg(test)]
+        #[cfg(feature = "test-support")]
         tokio::task::yield_now().await;
         let guard = self.connection_pool.lock();
         ConnectionPoolGuard {
@@ -267,13 +267,13 @@ impl Deref for DbHandle {
 pub struct Server {
     id: parking_lot::Mutex<ServerId>,
     peer: Arc<Peer>,
-    pub(crate) connection_pool: Arc<parking_lot::Mutex<ConnectionPool>>,
+    pub connection_pool: Arc<parking_lot::Mutex<ConnectionPool>>,
     app_state: Arc<AppState>,
     handlers: HashMap<TypeId, MessageHandler>,
     teardown: watch::Sender<bool>,
 }
 
-pub(crate) struct ConnectionPoolGuard<'a> {
+struct ConnectionPoolGuard<'a> {
     guard: parking_lot::MutexGuard<'a, ConnectionPool>,
     _not_send: PhantomData<Rc<()>>,
 }
@@ -651,7 +651,7 @@ impl Server {
         let _ = self.teardown.send(true);
     }
 
-    #[cfg(test)]
+    #[cfg(feature = "test-support")]
     pub fn reset(&self, id: ServerId) {
         self.teardown();
         *self.id.lock() = id;
@@ -659,7 +659,7 @@ impl Server {
         let _ = self.teardown.send(false);
     }
 
-    #[cfg(test)]
+    #[cfg(feature = "test-support")]
     pub fn id(&self) -> ServerId {
         *self.id.lock()
     }
@@ -1013,7 +1013,7 @@ impl DerefMut for ConnectionPoolGuard<'_> {
 
 impl Drop for ConnectionPoolGuard<'_> {
     fn drop(&mut self) {
-        #[cfg(test)]
+        #[cfg(feature = "test-support")]
         self.check_invariants();
     }
 }

crates/collab/src/rpc/connection_pool.rs 🔗

@@ -168,7 +168,7 @@ impl ConnectionPool {
             .is_empty()
     }
 
-    #[cfg(test)]
+    #[cfg(feature = "test-support")]
     pub fn check_invariants(&self) {
         for (connection_id, connection) in &self.connections {
             assert!(

crates/collab/src/tests.rs 🔗

@@ -1,51 +0,0 @@
-use call::Room;
-use client::ChannelId;
-use gpui::{Entity, TestAppContext};
-
-mod agent_sharing_tests;
-mod channel_buffer_tests;
-mod channel_guest_tests;
-mod channel_tests;
-mod editor_tests;
-mod following_tests;
-mod git_tests;
-mod integration_tests;
-mod notification_tests;
-mod random_channel_buffer_tests;
-mod random_project_collaboration_tests;
-mod randomized_test_helpers;
-mod remote_editing_collaboration_tests;
-mod test_server;
-
-pub use randomized_test_helpers::{
-    RandomizedTest, TestError, UserTestPlan, run_randomized_test, save_randomized_test_plan,
-};
-pub use test_server::{TestClient, TestServer};
-
-#[derive(Debug, Eq, PartialEq)]
-struct RoomParticipants {
-    remote: Vec<String>,
-    pending: Vec<String>,
-}
-
-fn room_participants(room: &Entity<Room>, cx: &mut TestAppContext) -> RoomParticipants {
-    room.read_with(cx, |room, _| {
-        let mut remote = room
-            .remote_participants()
-            .values()
-            .map(|participant| participant.user.github_login.clone().to_string())
-            .collect::<Vec<_>>();
-        let mut pending = room
-            .pending_participants()
-            .iter()
-            .map(|user| user.github_login.clone().to_string())
-            .collect::<Vec<_>>();
-        remote.sort();
-        pending.sort();
-        RoomParticipants { remote, pending }
-    })
-}
-
-fn channel_id(room: &Entity<Room>, cx: &mut TestAppContext) -> Option<ChannelId> {
-    cx.read(|cx| room.read(cx).channel_id())
-}

crates/collab/src/tests/debug_panel_tests.rs 🔗

@@ -1,2449 +0,0 @@
-use call::ActiveCall;
-use dap::DebugRequestType;
-use dap::requests::{Initialize, Launch, StackTrace};
-use dap::{SourceBreakpoint, requests::SetBreakpoints};
-use debugger_ui::debugger_panel::DebugPanel;
-use debugger_ui::session::DebugSession;
-use editor::Editor;
-use gpui::{Entity, TestAppContext, VisualTestContext};
-use project::{Project, ProjectPath, WorktreeId};
-use serde_json::json;
-use std::sync::Arc;
-use std::{
-    path::Path,
-    sync::atomic::{AtomicBool, Ordering},
-};
-use workspace::{Workspace, dock::Panel};
-
-use super::{TestClient, TestServer};
-
-pub fn init_test(cx: &mut gpui::TestAppContext) {
-    zlog::init_test();
-
-    cx.update(|cx| {
-        theme::init(theme::LoadThemes::JustBase, cx);
-        command_palette_hooks::init(cx);
-        debugger_ui::init(cx);
-        editor::init(cx);
-    });
-}
-
-async fn add_debugger_panel(workspace: &Entity<Workspace>, cx: &mut VisualTestContext) {
-    let debugger_panel = workspace
-        .update_in(cx, |_workspace, window, cx| {
-            cx.spawn_in(window, DebugPanel::load)
-        })
-        .await
-        .unwrap();
-
-    workspace.update_in(cx, |workspace, window, cx| {
-        workspace.add_panel(debugger_panel, window, cx);
-    });
-}
-
-pub fn _active_session(
-    workspace: Entity<Workspace>,
-    cx: &mut VisualTestContext,
-) -> Entity<DebugSession> {
-    workspace.update_in(cx, |workspace, _window, cx| {
-        let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-        debug_panel
-            .update(cx, |this, cx| this.active_session(cx))
-            .unwrap()
-    })
-}
-
-struct ZedInstance<'a> {
-    client: TestClient,
-    project: Option<Entity<Project>>,
-    active_call: Entity<ActiveCall>,
-    cx: &'a mut TestAppContext,
-}
-
-impl<'a> ZedInstance<'a> {
-    fn new(client: TestClient, cx: &'a mut TestAppContext) -> Self {
-        ZedInstance {
-            project: None,
-            client,
-            active_call: cx.read(ActiveCall::global),
-            cx,
-        }
-    }
-
-    async fn host_project(
-        &mut self,
-        project_files: Option<serde_json::Value>,
-    ) -> (u64, WorktreeId) {
-        let (project, worktree_id) = self.client.build_local_project("/project", self.cx).await;
-        self.active_call
-            .update(self.cx, |call, cx| call.set_location(Some(&project), cx))
-            .await
-            .unwrap();
-
-        if let Some(tree) = project_files {
-            self.client.fs().insert_tree("/project", tree).await;
-        }
-
-        self.project = Some(project.clone());
-
-        let project_id = self
-            .active_call
-            .update(self.cx, |call, cx| call.share_project(project, cx))
-            .await
-            .unwrap();
-
-        (project_id, worktree_id)
-    }
-
-    async fn join_project(&mut self, project_id: u64) {
-        let remote_project = self.client.join_remote_project(project_id, self.cx).await;
-        self.project = Some(remote_project);
-
-        self.active_call
-            .update(self.cx, |call, cx| {
-                call.set_location(self.project.as_ref(), cx)
-            })
-            .await
-            .unwrap();
-    }
-
-    async fn expand(
-        &'a mut self,
-    ) -> (
-        &'a TestClient,
-        Entity<Workspace>,
-        Entity<Project>,
-        &'a mut VisualTestContext,
-    ) {
-        let (workspace, cx) = self.client.build_workspace(
-            self.project
-                .as_ref()
-                .expect("Project should be hosted or built before expanding"),
-            self.cx,
-        );
-        add_debugger_panel(&workspace, cx).await;
-        (&self.client, workspace, self.project.clone().unwrap(), cx)
-    }
-}
-
-async fn _setup_three_member_test<'a, 'b, 'c>(
-    server: &mut TestServer,
-    host_cx: &'a mut TestAppContext,
-    first_remote_cx: &'b mut TestAppContext,
-    second_remote_cx: &'c mut TestAppContext,
-) -> (ZedInstance<'a>, ZedInstance<'b>, ZedInstance<'c>) {
-    let host_client = server.create_client(host_cx, "user_host").await;
-    let first_remote_client = server.create_client(first_remote_cx, "user_remote_1").await;
-    let second_remote_client = server
-        .create_client(second_remote_cx, "user_remote_2")
-        .await;
-
-    init_test(host_cx);
-    init_test(first_remote_cx);
-    init_test(second_remote_cx);
-
-    server
-        .create_room(&mut [
-            (&host_client, host_cx),
-            (&first_remote_client, first_remote_cx),
-            (&second_remote_client, second_remote_cx),
-        ])
-        .await;
-
-    let host_zed = ZedInstance::new(host_client, host_cx);
-    let first_remote_zed = ZedInstance::new(first_remote_client, first_remote_cx);
-    let second_remote_zed = ZedInstance::new(second_remote_client, second_remote_cx);
-
-    (host_zed, first_remote_zed, second_remote_zed)
-}
-
-async fn setup_two_member_test<'a, 'b>(
-    server: &mut TestServer,
-    host_cx: &'a mut TestAppContext,
-    remote_cx: &'b mut TestAppContext,
-) -> (ZedInstance<'a>, ZedInstance<'b>) {
-    let host_client = server.create_client(host_cx, "user_host").await;
-    let remote_client = server.create_client(remote_cx, "user_remote").await;
-
-    init_test(host_cx);
-    init_test(remote_cx);
-
-    server
-        .create_room(&mut [(&host_client, host_cx), (&remote_client, remote_cx)])
-        .await;
-
-    let host_zed = ZedInstance::new(host_client, host_cx);
-    let remote_zed = ZedInstance::new(remote_client, remote_cx);
-
-    (host_zed, remote_zed)
-}
-
-#[gpui::test]
-async fn test_debug_panel_item_opens_on_remote(
-    host_cx: &mut TestAppContext,
-    remote_cx: &mut TestAppContext,
-) {
-    let executor = host_cx.executor();
-    let mut server = TestServer::start(executor).await;
-
-    let (mut host_zed, mut remote_zed) =
-        setup_two_member_test(&mut server, host_cx, remote_cx).await;
-
-    let (host_project_id, _) = host_zed.host_project(None).await;
-    remote_zed.join_project(host_project_id).await;
-
-    let (_client_host, _host_workspace, host_project, host_cx) = host_zed.expand().await;
-    let (_client_remote, remote_workspace, _remote_project, remote_cx) = remote_zed.expand().await;
-
-    remote_cx.run_until_parked();
-
-    let task = host_project.update(host_cx, |project, cx| {
-        project.start_debug_session(dap::test_config(DebugRequestType::Launch, None, None), cx)
-    });
-
-    let session = task.await.unwrap();
-    let client = session.read_with(host_cx, |project, _| project.adapter_client().unwrap());
-
-    client
-        .on_request::<Initialize, _>(move |_, _| {
-            Ok(dap::Capabilities {
-                supports_step_back: Some(false),
-                ..Default::default()
-            })
-        })
-        .await;
-
-    client.on_request::<Launch, _>(move |_, _| Ok(())).await;
-
-    client
-        .on_request::<StackTrace, _>(move |_, _| {
-            Ok(dap::StackTraceResponse {
-                stack_frames: Vec::default(),
-                total_frames: None,
-            })
-        })
-        .await;
-
-    client
-        .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-            reason: dap::StoppedEventReason::Pause,
-            description: None,
-            thread_id: Some(1),
-            preserve_focus_hint: None,
-            text: None,
-            all_threads_stopped: None,
-            hit_breakpoint_ids: None,
-        }))
-        .await;
-
-    host_cx.run_until_parked();
-    remote_cx.run_until_parked();
-
-    remote_workspace.update(remote_cx, |workspace, cx| {
-        let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-        let _active_session = debug_panel
-            .update(cx, |this, cx| this.active_session(cx))
-            .unwrap();
-
-        assert_eq!(
-            1,
-            debug_panel.update(cx, |this, cx| this.pane().unwrap().read(cx).items_len())
-        );
-        // assert_eq!(client.id(), active_session.read(cx).());
-        // assert_eq!(1, active_session.read(cx).thread_id().0);
-        // todo(debugger) check selected thread id
-    });
-
-    let shutdown_client = host_project.update(host_cx, |project, cx| {
-        project.dap_store().update(cx, |dap_store, cx| {
-            dap_store.shutdown_session(session.read(cx).session_id(), cx)
-        })
-    });
-
-    shutdown_client.await.unwrap();
-}
-
-#[gpui::test]
-async fn test_active_debug_panel_item_set_on_join_project(
-    host_cx: &mut TestAppContext,
-    remote_cx: &mut TestAppContext,
-) {
-    let executor = host_cx.executor();
-    let mut server = TestServer::start(executor).await;
-
-    let (mut host_zed, mut remote_zed) =
-        setup_two_member_test(&mut server, host_cx, remote_cx).await;
-
-    let (host_project_id, _) = host_zed.host_project(None).await;
-
-    let (_client_host, _host_workspace, host_project, host_cx) = host_zed.expand().await;
-
-    host_cx.run_until_parked();
-
-    let task = host_project.update(host_cx, |project, cx| {
-        project.start_debug_session(dap::test_config(DebugRequestType::Launch, None, None), cx)
-    });
-
-    let session = task.await.unwrap();
-    let client = session.read_with(host_cx, |project, _| project.adapter_client().unwrap());
-
-    client
-        .on_request::<Initialize, _>(move |_, _| {
-            Ok(dap::Capabilities {
-                supports_step_back: Some(false),
-                ..Default::default()
-            })
-        })
-        .await;
-
-    client.on_request::<Launch, _>(move |_, _| Ok(())).await;
-
-    client
-        .on_request::<StackTrace, _>(move |_, _| {
-            Ok(dap::StackTraceResponse {
-                stack_frames: Vec::default(),
-                total_frames: None,
-            })
-        })
-        .await;
-
-    client
-        .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-            reason: dap::StoppedEventReason::Pause,
-            description: None,
-            thread_id: Some(1),
-            preserve_focus_hint: None,
-            text: None,
-            all_threads_stopped: None,
-            hit_breakpoint_ids: None,
-        }))
-        .await;
-
-    // Give host_client time to send a debug panel item to collab server
-    host_cx.run_until_parked();
-
-    remote_zed.join_project(host_project_id).await;
-    let (_client_remote, remote_workspace, _remote_project, remote_cx) = remote_zed.expand().await;
-
-    host_cx.run_until_parked();
-    remote_cx.run_until_parked();
-
-    remote_workspace.update(remote_cx, |workspace, cx| {
-        let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-        let _active_session = debug_panel
-            .update(cx, |this, cx| this.active_session(cx))
-            .unwrap();
-
-        assert_eq!(
-            1,
-            debug_panel.update(cx, |this, cx| this.pane().unwrap().read(cx).items_len())
-        );
-        // assert_eq!(cl, active_session.read(cx).client_id());
-        // assert_eq!(1, active_session.read(cx).thread_id().0);
-        // todo(debugger)
-    });
-
-    let shutdown_client = host_project.update(host_cx, |project, cx| {
-        project.dap_store().update(cx, |dap_store, cx| {
-            dap_store.shutdown_session(session.read(cx).session_id(), cx)
-        })
-    });
-
-    shutdown_client.await.unwrap();
-
-    remote_cx.run_until_parked();
-
-    // assert we don't have a debug panel item anymore because the client shutdown
-    remote_workspace.update(remote_cx, |workspace, cx| {
-        let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-
-        debug_panel.update(cx, |this, cx| {
-            assert!(this.active_session(cx).is_none());
-            assert_eq!(0, this.pane().unwrap().read(cx).items_len());
-        });
-    });
-}
-
-#[gpui::test]
-async fn test_debug_panel_remote_button_presses(
-    _host_cx: &mut TestAppContext,
-    _remote_cx: &mut TestAppContext,
-) {
-    unimplemented!("Collab is still being refactored");
-    // let executor = host_cx.executor();
-    // let mut server = TestServer::start(executor).await;
-
-    // let (mut host_zed, mut remote_zed) =
-    //     setup_two_member_test(&mut server, host_cx, remote_cx).await;
-
-    // let (host_project_id, _) = host_zed.host_project(None).await;
-    // remote_zed.join_project(host_project_id).await;
-
-    // let (_client_host, host_workspace, host_project, host_cx) = host_zed.expand().await;
-    // let (_client_remote, remote_workspace, _remote_project, remote_cx) = remote_zed.expand().await;
-
-    // let task = host_project.update(host_cx, |project, cx| {
-    //     project.start_debug_session(dap::test_config(None), cx)
-    // });
-
-    // let session = task.await.unwrap();
-    // let client = session.read_with(host_cx, |project, _| project.adapter_client().unwrap());
-
-    // client
-    //     .on_request::<Initialize, _>(move |_, _| {
-    //         Ok(dap::Capabilities {
-    //             supports_step_back: Some(true),
-    //             ..Default::default()
-    //         })
-    //     })
-    //     .await;
-
-    // client.on_request::<Launch, _>(move |_, _| Ok(())).await;
-
-    // client
-    //     .on_request::<StackTrace, _>(move |_, _| {
-    //         Ok(dap::StackTraceResponse {
-    //             stack_frames: Vec::default(),
-    //             total_frames: None,
-    //         })
-    //     })
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-    //         reason: dap::StoppedEventReason::Pause,
-    //         description: None,
-    //         thread_id: Some(1),
-    //         preserve_focus_hint: None,
-    //         text: None,
-    //         all_threads_stopped: None,
-    //         hit_breakpoint_ids: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .on_request::<dap::requests::Continue, _>(move |_, _| {
-    //         Ok(dap::ContinueResponse {
-    //             all_threads_continued: Some(true),
-    //         })
-    //     })
-    //     .await;
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // let remote_debug_item = remote_workspace.update(remote_cx, |workspace, cx| {
-    //     let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-    //     let active_session = debug_panel
-    //         .update(cx, |this, cx| this.active_session(cx))
-    //         .unwrap();
-
-    //     assert_eq!(
-    //         1,
-    //         debug_panel.update(cx, |this, cx| this.pane().unwrap().read(cx).items_len())
-    //     );
-    //     // assert_eq!(client.id(), active_session.read(cx).client_id());
-    //     // assert_eq!(1, active_session.read(cx).thread_id().0);
-    //     // todo(debugger)
-    //     active_session
-    // });
-
-    // let local_debug_item = host_workspace.update(host_cx, |workspace, cx| {
-    //     let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-    //     let active_session = debug_panel
-    //         .update(cx, |this, cx| this.active_session(cx))
-    //         .unwrap();
-
-    //     assert_eq!(
-    //         1,
-    //         debug_panel.update(cx, |this, cx| this.pane().unwrap().read(cx).items_len())
-    //     );
-    //     // assert_eq!(client.id(), active_session.read(cx).client_id());
-    //     // assert_eq!(1, active_session.read(cx).thread_id().0);
-    //     // todo(debugger)
-    //     active_session
-    // });
-
-    // remote_debug_item.update(remote_cx, |this, cx| {
-    //     this.continue_thread(cx);
-    // });
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // local_debug_item.update(host_cx, |debug_panel_item, cx| {
-    //     assert_eq!(
-    //         debugger_ui::debugger_panel::ThreadStatus::Running,
-    //         debug_panel_item.thread_state().read(cx).status,
-    //     );
-    // });
-
-    // remote_debug_item.update(remote_cx, |debug_panel_item, cx| {
-    //     assert_eq!(
-    //         debugger_ui::debugger_panel::ThreadStatus::Running,
-    //         debug_panel_item.thread_state().read(cx).status,
-    //     );
-    // });
-
-    // client
-    //     .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-    //         reason: dap::StoppedEventReason::Pause,
-    //         description: None,
-    //         thread_id: Some(1),
-    //         preserve_focus_hint: None,
-    //         text: None,
-    //         all_threads_stopped: None,
-    //         hit_breakpoint_ids: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .on_request::<StackTrace, _>(move |_, _| {
-    //         Ok(dap::StackTraceResponse {
-    //             stack_frames: Vec::default(),
-    //             total_frames: None,
-    //         })
-    //     })
-    //     .await;
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // local_debug_item.update(host_cx, |debug_panel_item, cx| {
-    //     assert_eq!(
-    //         debugger_ui::debugger_panel::ThreadStatus::Stopped,
-    //         debug_panel_item.thread_state().read(cx).status,
-    //     );
-    // });
-
-    // remote_debug_item.update(remote_cx, |debug_panel_item, cx| {
-    //     assert_eq!(
-    //         debugger_ui::debugger_panel::ThreadStatus::Stopped,
-    //         debug_panel_item.thread_state().read(cx).status,
-    //     );
-    // });
-
-    // client
-    //     .on_request::<dap::requests::Continue, _>(move |_, _| {
-    //         Ok(dap::ContinueResponse {
-    //             all_threads_continued: Some(true),
-    //         })
-    //     })
-    //     .await;
-
-    // local_debug_item.update(host_cx, |this, cx| {
-    //     this.continue_thread(cx);
-    // });
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // local_debug_item.update(host_cx, |debug_panel_item, cx| {
-    //     assert_eq!(
-    //         debugger_ui::debugger_panel::ThreadStatus::Running,
-    //         debug_panel_item.thread_state().read(cx).status,
-    //     );
-    // });
-
-    // remote_debug_item.update(remote_cx, |debug_panel_item, cx| {
-    //     assert_eq!(
-    //         debugger_ui::debugger_panel::ThreadStatus::Running,
-    //         debug_panel_item.thread_state().read(cx).status,
-    //     );
-    // });
-
-    // client
-    //     .on_request::<dap::requests::Pause, _>(move |_, _| Ok(()))
-    //     .await;
-
-    // client
-    //     .on_request::<StackTrace, _>(move |_, _| {
-    //         Ok(dap::StackTraceResponse {
-    //             stack_frames: Vec::default(),
-    //             total_frames: None,
-    //         })
-    //     })
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-    //         reason: dap::StoppedEventReason::Pause,
-    //         description: None,
-    //         thread_id: Some(1),
-    //         preserve_focus_hint: None,
-    //         text: None,
-    //         all_threads_stopped: None,
-    //         hit_breakpoint_ids: None,
-    //     }))
-    //     .await;
-
-    // remote_debug_item.update(remote_cx, |this, cx| {
-    //     this.pause_thread(cx);
-    // });
-
-    // remote_cx.run_until_parked();
-    // host_cx.run_until_parked();
-
-    // client
-    //     .on_request::<dap::requests::StepOut, _>(move |_, _| Ok(()))
-    //     .await;
-
-    // remote_debug_item.update(remote_cx, |this, cx| {
-    //     this.step_out(cx);
-    // });
-
-    // client
-    //     .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-    //         reason: dap::StoppedEventReason::Pause,
-    //         description: None,
-    //         thread_id: Some(1),
-    //         preserve_focus_hint: None,
-    //         text: None,
-    //         all_threads_stopped: None,
-    //         hit_breakpoint_ids: None,
-    //     }))
-    //     .await;
-
-    // remote_cx.run_until_parked();
-    // host_cx.run_until_parked();
-
-    // client
-    //     .on_request::<dap::requests::Next, _>(move |_, _| Ok(()))
-    //     .await;
-
-    // remote_debug_item.update(remote_cx, |this, cx| {
-    //     this.step_over(cx);
-    // });
-
-    // client
-    //     .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-    //         reason: dap::StoppedEventReason::Pause,
-    //         description: None,
-    //         thread_id: Some(1),
-    //         preserve_focus_hint: None,
-    //         text: None,
-    //         all_threads_stopped: None,
-    //         hit_breakpoint_ids: None,
-    //     }))
-    //     .await;
-
-    // remote_cx.run_until_parked();
-    // host_cx.run_until_parked();
-
-    // client
-    //     .on_request::<dap::requests::StepIn, _>(move |_, _| Ok(()))
-    //     .await;
-
-    // remote_debug_item.update(remote_cx, |this, cx| {
-    //     this.step_in(cx);
-    // });
-
-    // client
-    //     .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-    //         reason: dap::StoppedEventReason::Pause,
-    //         description: None,
-    //         thread_id: Some(1),
-    //         preserve_focus_hint: None,
-    //         text: None,
-    //         all_threads_stopped: None,
-    //         hit_breakpoint_ids: None,
-    //     }))
-    //     .await;
-
-    // remote_cx.run_until_parked();
-    // host_cx.run_until_parked();
-
-    // client
-    //     .on_request::<dap::requests::StepBack, _>(move |_, _| Ok(()))
-    //     .await;
-
-    // remote_debug_item.update(remote_cx, |this, cx| {
-    //     this.step_back(cx);
-    // });
-
-    // client
-    //     .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-    //         reason: dap::StoppedEventReason::Pause,
-    //         description: None,
-    //         thread_id: Some(1),
-    //         preserve_focus_hint: None,
-    //         text: None,
-    //         all_threads_stopped: None,
-    //         hit_breakpoint_ids: None,
-    //     }))
-    //     .await;
-
-    // remote_cx.run_until_parked();
-    // host_cx.run_until_parked();
-
-    // remote_debug_item.update(remote_cx, |this, cx| {
-    //     this.stop_thread(cx);
-    // });
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // // assert we don't have a debug panel item anymore because the client shutdown
-    // remote_workspace.update(remote_cx, |workspace, cx| {
-    //     let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-
-    //     debug_panel.update(cx, |this, cx| {
-    //         assert!(this.active_session(cx).is_none());
-    //         assert_eq!(0, this.pane().unwrap().read(cx).items_len());
-    //     });
-    // });
-}
-
-#[gpui::test]
-async fn test_restart_stack_frame(_host_cx: &mut TestAppContext, _remote_cx: &mut TestAppContext) {
-    unimplemented!("Collab is still being refactored");
-    // let executor = host_cx.executor();
-    // let mut server = TestServer::start(executor).await;
-
-    // let (mut host_zed, mut remote_zed) =
-    //     setup_two_member_test(&mut server, host_cx, remote_cx).await;
-
-    // let (host_project_id, _) = host_zed.host_project(None).await;
-    // remote_zed.join_project(host_project_id).await;
-
-    // let (_client_host, _host_workspace, host_project, host_cx) = host_zed.expand().await;
-    // let (_client_remote, remote_workspace, _remote_project, remote_cx) = remote_zed.expand().await;
-
-    // let called_restart_frame = Arc::new(AtomicBool::new(false));
-
-    // let task = host_project.update(host_cx, |project, cx| {
-    //     project.start_debug_session(dap::test_config(None), cx)
-    // });
-
-    // let session = task.await.unwrap();
-    // let client = session.read(cx).adapter_client().unwrap();
-
-    // client
-    //     .on_request::<Initialize, _>(move |_, _| {
-    //         Ok(dap::Capabilities {
-    //             supports_restart_frame: Some(true),
-    //             ..Default::default()
-    //         })
-    //     })
-    //     .await;
-
-    // client.on_request::<Launch, _>(move |_, _| Ok(())).await;
-
-    // let stack_frames = vec![StackFrame {
-    //     id: 1,
-    //     name: "Stack Frame 1".into(),
-    //     source: Some(dap::Source {
-    //         name: Some("test.js".into()),
-    //         path: Some("/project/src/test.js".into()),
-    //         source_reference: None,
-    //         presentation_hint: None,
-    //         origin: None,
-    //         sources: None,
-    //         adapter_data: None,
-    //         checksums: None,
-    //     }),
-    //     line: 3,
-    //     column: 1,
-    //     end_line: None,
-    //     end_column: None,
-    //     can_restart: None,
-    //     instruction_pointer_reference: None,
-    //     module_id: None,
-    //     presentation_hint: None,
-    // }];
-
-    // client
-    //     .on_request::<StackTrace, _>({
-    //         let stack_frames = Arc::new(stack_frames.clone());
-    //         move |_, args| {
-    //             assert_eq!(1, args.thread_id);
-
-    //             Ok(dap::StackTraceResponse {
-    //                 stack_frames: (*stack_frames).clone(),
-    //                 total_frames: None,
-    //             })
-    //         }
-    //     })
-    //     .await;
-
-    // client
-    //     .on_request::<RestartFrame, _>({
-    //         let called_restart_frame = called_restart_frame.clone();
-    //         move |_, args| {
-    //             assert_eq!(1, args.frame_id);
-
-    //             called_restart_frame.store(true, Ordering::SeqCst);
-
-    //             Ok(())
-    //         }
-    //     })
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-    //         reason: dap::StoppedEventReason::Pause,
-    //         description: None,
-    //         thread_id: Some(1),
-    //         preserve_focus_hint: None,
-    //         text: None,
-    //         all_threads_stopped: None,
-    //         hit_breakpoint_ids: None,
-    //     }))
-    //     .await;
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // // try to restart stack frame 1 from the guest side
-    // remote_workspace.update(remote_cx, |workspace, cx| {
-    //     let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-    //     let active_session = debug_panel
-    //         .update(cx, |this, cx| this.active_session(cx))
-    //         .unwrap();
-
-    //     active_session.update(cx, |debug_panel_item, cx| {
-    //         debug_panel_item
-    //             .stack_frame_list()
-    //             .update(cx, |stack_frame_list, cx| {
-    //                 stack_frame_list.restart_stack_frame(1, cx);
-    //             });
-    //     });
-    // });
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // assert!(
-    //     called_restart_frame.load(std::sync::atomic::Ordering::SeqCst),
-    //     "Restart stack frame was not called"
-    // );
-
-    // let shutdown_client = host_project.update(host_cx, |project, cx| {
-    //     project.dap_store().update(cx, |dap_store, cx| {
-    //         dap_store.shutdown_session(&session.read(cx).session_id(), cx)
-    //     })
-    // });
-
-    // shutdown_client.await.unwrap();
-}
-
-#[gpui::test]
-async fn test_updated_breakpoints_send_to_dap(
-    host_cx: &mut TestAppContext,
-    remote_cx: &mut TestAppContext,
-) {
-    let executor = host_cx.executor();
-    let mut server = TestServer::start(executor).await;
-
-    let (mut host_zed, mut remote_zed) =
-        setup_two_member_test(&mut server, host_cx, remote_cx).await;
-
-    let (host_project_id, worktree_id) = host_zed
-        .host_project(Some(json!({"test.txt": "one\ntwo\nthree\nfour\nfive"})))
-        .await;
-
-    remote_zed.join_project(host_project_id).await;
-
-    let (_client_host, host_workspace, host_project, host_cx) = host_zed.expand().await;
-    let (_client_remote, remote_workspace, _remote_project, remote_cx) = remote_zed.expand().await;
-
-    let project_path = ProjectPath {
-        worktree_id,
-        path: Arc::from(Path::new(&"test.txt")),
-    };
-
-    let task = host_project.update(host_cx, |project, cx| {
-        project.start_debug_session(dap::test_config(DebugRequestType::Launch, None, None), cx)
-    });
-
-    let session = task.await.unwrap();
-    let client = session.read_with(host_cx, |project, _| project.adapter_client().unwrap());
-
-    client
-        .on_request::<Initialize, _>(move |_, _| {
-            Ok(dap::Capabilities {
-                supports_restart_frame: Some(true),
-                ..Default::default()
-            })
-        })
-        .await;
-
-    client.on_request::<Launch, _>(move |_, _| Ok(())).await;
-    client
-        .on_request::<StackTrace, _>(move |_, _| {
-            Ok(dap::StackTraceResponse {
-                stack_frames: Vec::default(),
-                total_frames: None,
-            })
-        })
-        .await;
-
-    let called_set_breakpoints = Arc::new(AtomicBool::new(false));
-    client
-        .on_request::<SetBreakpoints, _>({
-            let called_set_breakpoints = called_set_breakpoints.clone();
-            move |_, args| {
-                assert_eq!("/project/test.txt", args.source.path.unwrap());
-                assert_eq!(
-                    vec![SourceBreakpoint {
-                        line: 3,
-                        column: None,
-                        condition: None,
-                        hit_condition: None,
-                        log_message: None,
-                        mode: None
-                    }],
-                    args.breakpoints.unwrap()
-                );
-                // assert!(!args.source_modified.unwrap());
-                // todo(debugger): Implement source_modified handling
-
-                called_set_breakpoints.store(true, Ordering::SeqCst);
-
-                Ok(dap::SetBreakpointsResponse {
-                    breakpoints: Vec::default(),
-                })
-            }
-        })
-        .await;
-
-    client
-        .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-            reason: dap::StoppedEventReason::Pause,
-            description: None,
-            thread_id: Some(1),
-            preserve_focus_hint: None,
-            text: None,
-            all_threads_stopped: None,
-            hit_breakpoint_ids: None,
-        }))
-        .await;
-
-    host_cx.run_until_parked();
-    remote_cx.run_until_parked();
-
-    // Client B opens an editor.
-    let editor_b = remote_workspace
-        .update_in(remote_cx, |workspace, window, cx| {
-            workspace.open_path(project_path.clone(), None, true, window, cx)
-        })
-        .await
-        .unwrap()
-        .downcast::<Editor>()
-        .unwrap();
-
-    editor_b.update_in(remote_cx, |editor, window, cx| {
-        editor.move_down(&zed_actions::editor::MoveDown, window, cx);
-        editor.move_down(&zed_actions::editor::MoveDown, window, cx);
-        editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx);
-    });
-
-    // Client A opens an editor.
-    let editor_a = host_workspace
-        .update_in(host_cx, |workspace, window, cx| {
-            workspace.open_path(project_path.clone(), None, true, window, cx)
-        })
-        .await
-        .unwrap()
-        .downcast::<Editor>()
-        .unwrap();
-
-    host_cx.run_until_parked();
-    remote_cx.run_until_parked();
-
-    let called_set_breakpoints = Arc::new(AtomicBool::new(false));
-    client
-        .on_request::<SetBreakpoints, _>({
-            let called_set_breakpoints = called_set_breakpoints.clone();
-            move |_, args| {
-                assert_eq!("/project/test.txt", args.source.path.unwrap());
-                assert!(args.breakpoints.unwrap().is_empty());
-                // assert!(!args.source_modified.unwrap());
-                // todo(debugger) Implement source modified support
-
-                called_set_breakpoints.store(true, Ordering::SeqCst);
-
-                Ok(dap::SetBreakpointsResponse {
-                    breakpoints: Vec::default(),
-                })
-            }
-        })
-        .await;
-
-    // remove the breakpoint that client B added
-    editor_a.update_in(host_cx, |editor, window, cx| {
-        editor.move_down(&zed_actions::editor::MoveDown, window, cx);
-        editor.move_down(&zed_actions::editor::MoveDown, window, cx);
-        editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx);
-    });
-
-    host_cx.run_until_parked();
-    remote_cx.run_until_parked();
-
-    assert!(
-        called_set_breakpoints.load(std::sync::atomic::Ordering::SeqCst),
-        "SetBreakpoint request must be called"
-    );
-
-    let called_set_breakpoints = Arc::new(AtomicBool::new(false));
-    client
-        .on_request::<SetBreakpoints, _>({
-            let called_set_breakpoints = called_set_breakpoints.clone();
-            move |_, args| {
-                assert_eq!("/project/test.txt", args.source.path.unwrap());
-                let mut breakpoints = args.breakpoints.unwrap();
-                breakpoints.sort_by_key(|b| b.line);
-                assert_eq!(
-                    vec![
-                        SourceBreakpoint {
-                            line: 2,
-                            column: None,
-                            condition: None,
-                            hit_condition: None,
-                            log_message: None,
-                            mode: None
-                        },
-                        SourceBreakpoint {
-                            line: 3,
-                            column: None,
-                            condition: None,
-                            hit_condition: None,
-                            log_message: None,
-                            mode: None
-                        }
-                    ],
-                    breakpoints
-                );
-                // assert!(!args.source_modified.unwrap());
-                // todo(debugger) Implement source modified support
-
-                called_set_breakpoints.store(true, Ordering::SeqCst);
-
-                Ok(dap::SetBreakpointsResponse {
-                    breakpoints: Vec::default(),
-                })
-            }
-        })
-        .await;
-
-    // Add our own breakpoint now
-    editor_a.update_in(host_cx, |editor, window, cx| {
-        editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx);
-        editor.move_up(&zed_actions::editor::MoveUp, window, cx);
-        editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx);
-    });
-
-    host_cx.run_until_parked();
-    remote_cx.run_until_parked();
-
-    assert!(
-        called_set_breakpoints.load(std::sync::atomic::Ordering::SeqCst),
-        "SetBreakpoint request must be called"
-    );
-
-    let shutdown_client = host_project.update(host_cx, |project, cx| {
-        project.dap_store().update(cx, |dap_store, cx| {
-            dap_store.shutdown_session(session.read(cx).session_id(), cx)
-        })
-    });
-
-    shutdown_client.await.unwrap();
-}
-
-#[gpui::test]
-async fn test_module_list(
-    _host_cx: &mut TestAppContext,
-    _remote_cx: &mut TestAppContext,
-    _late_join_cx: &mut TestAppContext,
-) {
-    unimplemented!("Collab is still being refactored");
-    // let executor = host_cx.executor();
-    // let mut server = TestServer::start(executor).await;
-
-    // let (mut host_zed, mut remote_zed, mut late_join_zed) =
-    //     setup_three_member_test(&mut server, host_cx, remote_cx, late_join_cx).await;
-
-    // let (host_project_id, _worktree_id) = host_zed.host_project(None).await;
-
-    // remote_zed.join_project(host_project_id).await;
-
-    // let (_client_host, host_workspace, host_project, host_cx) = host_zed.expand().await;
-    // let (_client_remote, remote_workspace, _remote_project, remote_cx) = remote_zed.expand().await;
-
-    // let task = host_project.update(host_cx, |project, cx| {
-    //     project.start_debug_session(dap::test_config(None), cx)
-    // });
-
-    // let session = task.await.unwrap();
-    // let client = session.read_with(host_cx, |project, _| project.adapter_client().unwrap());
-
-    // let called_initialize = Arc::new(AtomicBool::new(false));
-
-    // client
-    //     .on_request::<Initialize, _>({
-    //         let called_initialize = called_initialize.clone();
-    //         move |_, _| {
-    //             called_initialize.store(true, Ordering::SeqCst);
-    //             Ok(dap::Capabilities {
-    //                 supports_restart_frame: Some(true),
-    //                 supports_modules_request: Some(true),
-    //                 ..Default::default()
-    //             })
-    //         }
-    //     })
-    //     .await;
-
-    // client.on_request::<Launch, _>(move |_, _| Ok(())).await;
-    // client
-    //     .on_request::<StackTrace, _>(move |_, _| {
-    //         Ok(dap::StackTraceResponse {
-    //             stack_frames: Vec::default(),
-    //             total_frames: None,
-    //         })
-    //     })
-    //     .await;
-
-    // let called_modules = Arc::new(AtomicBool::new(false));
-    // let modules = vec![
-    //     dap::Module {
-    //         id: dap::ModuleId::Number(1),
-    //         name: "First Module".into(),
-    //         address_range: None,
-    //         date_time_stamp: None,
-    //         path: None,
-    //         symbol_file_path: None,
-    //         symbol_status: None,
-    //         version: None,
-    //         is_optimized: None,
-    //         is_user_code: None,
-    //     },
-    //     dap::Module {
-    //         id: dap::ModuleId::Number(2),
-    //         name: "Second Module".into(),
-    //         address_range: None,
-    //         date_time_stamp: None,
-    //         path: None,
-    //         symbol_file_path: None,
-    //         symbol_status: None,
-    //         version: None,
-    //         is_optimized: None,
-    //         is_user_code: None,
-    //     },
-    // ];
-
-    // client
-    //     .on_request::<dap::requests::Modules, _>({
-    //         let called_modules = called_modules.clone();
-    //         let modules = modules.clone();
-    //         move |_, _| unsafe {
-    //             static mut REQUEST_COUNT: i32 = 1;
-    //             assert_eq!(
-    //                 1, REQUEST_COUNT,
-    //                 "This request should only be called once from the host"
-    //             );
-    //             REQUEST_COUNT += 1;
-    //             called_modules.store(true, Ordering::SeqCst);
-
-    //             Ok(dap::ModulesResponse {
-    //                 modules: modules.clone(),
-    //                 total_modules: Some(2u64),
-    //             })
-    //         }
-    //     })
-    //     .await;
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // assert!(
-    //     called_initialize.load(std::sync::atomic::Ordering::SeqCst),
-    //     "Request Initialize must be called"
-    // );
-
-    // client
-    //     .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-    //         reason: dap::StoppedEventReason::Pause,
-    //         description: None,
-    //         thread_id: Some(1),
-    //         preserve_focus_hint: None,
-    //         text: None,
-    //         all_threads_stopped: None,
-    //         hit_breakpoint_ids: None,
-    //     }))
-    //     .await;
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // assert!(
-    //     called_modules.load(std::sync::atomic::Ordering::SeqCst),
-    //     "Request Modules must be called"
-    // );
-
-    // host_workspace.update(host_cx, |workspace, cx| {
-    //     let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-    //     let debug_panel_item = debug_panel
-    //         .update(cx, |this, cx| this.active_session(cx))
-    //         .unwrap();
-
-    //     debug_panel_item.update(cx, |item, cx| {
-    //         assert_eq!(
-    //             true,
-    //             item.capabilities(cx).supports_modules_request.unwrap(),
-    //             "Local supports modules request should be true"
-    //         );
-
-    //         let local_module_list = item.module_list().update(cx, |list, cx| list.modules(cx));
-
-    //         assert_eq!(
-    //             2usize,
-    //             local_module_list.len(),
-    //             "Local module list should have two items in it"
-    //         );
-    //         assert_eq!(
-    //             modules.clone(),
-    //             local_module_list,
-    //             "Local module list should match module list from response"
-    //         );
-    //     })
-    // });
-
-    // remote_workspace.update(remote_cx, |workspace, cx| {
-    //     let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-    //     let debug_panel_item = debug_panel
-    //         .update(cx, |this, cx| this.active_session(cx))
-    //         .unwrap();
-
-    //     debug_panel_item.update(cx, |item, cx| {
-    //         assert_eq!(
-    //             true,
-    //             item.capabilities(cx).supports_modules_request.unwrap(),
-    //             "Remote capabilities supports modules request should be true"
-    //         );
-    //         let remote_module_list = item.module_list().update(cx, |list, cx| list.modules(cx));
-
-    //         assert_eq!(
-    //             2usize,
-    //             remote_module_list.len(),
-    //             "Remote module list should have two items in it"
-    //         );
-    //         assert_eq!(
-    //             modules.clone(),
-    //             remote_module_list,
-    //             "Remote module list should match module list from response"
-    //         );
-    //     })
-    // });
-
-    // late_join_zed.join_project(host_project_id).await;
-    // let (_late_join_client, late_join_workspace, _late_join_project, late_join_cx) =
-    //     late_join_zed.expand().await;
-
-    // late_join_workspace.update(late_join_cx, |workspace, cx| {
-    //     let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-    //     let debug_panel_item = debug_panel
-    //         .update(cx, |this, cx| this.active_session(cx))
-    //         .unwrap();
-
-    //     debug_panel_item.update(cx, |item, cx| {
-    //         assert_eq!(
-    //             true,
-    //             item.capabilities(cx).supports_modules_request.unwrap(),
-    //             "Remote (mid session join) capabilities supports modules request should be true"
-    //         );
-    //         let remote_module_list = item.module_list().update(cx, |list, cx| list.modules(cx));
-
-    //         assert_eq!(
-    //             2usize,
-    //             remote_module_list.len(),
-    //             "Remote (mid session join) module list should have two items in it"
-    //         );
-    //         assert_eq!(
-    //             modules.clone(),
-    //             remote_module_list,
-    //             "Remote (mid session join) module list should match module list from response"
-    //         );
-    //     })
-    // });
-
-    // let shutdown_client = host_project.update(host_cx, |project, cx| {
-    //     project.dap_store().update(cx, |dap_store, cx| {
-    //         dap_store.shutdown_session(&session.read(cx).id(), cx)
-    //     })
-    // });
-
-    // shutdown_client.await.unwrap();
-}
-
-// #[gpui::test]
-// async fn test_variable_list(
-//     host_cx: &mut TestAppContext,
-//     remote_cx: &mut TestAppContext,
-//     late_join_cx: &mut TestAppContext,
-// ) {
-//     let executor = host_cx.executor();
-//     let mut server = TestServer::start(executor).await;
-
-//     let (mut host_zed, mut remote_zed, mut late_join_zed) =
-//         setup_three_member_test(&mut server, host_cx, remote_cx, late_join_cx).await;
-
-//     let (host_project_id, _worktree_id) = host_zed
-//         .host_project(Some(json!({"test.txt": "one\ntwo\nthree\nfour\nfive"})))
-//         .await;
-
-//     remote_zed.join_project(host_project_id).await;
-
-//     let (_client_host, host_workspace, host_project, host_cx) = host_zed.expand().await;
-//     let (_client_remote, remote_workspace, _remote_project, remote_cx) = remote_zed.expand().await;
-
-//     let task = host_project.update(host_cx, |project, cx| {
-//         project.start_debug_session(
-//             dap::DebugAdapterConfig {
-//                 label: "test config".into(),
-//                 kind: dap::DebugAdapterKind::Fake,
-//                 request: dap::DebugRequestType::Launch,
-//                 program: None,
-//                 cwd: None,
-//                 initialize_args: None,
-//             },
-//             cx,
-//         )
-//     });
-
-//     let (session, client) = task.await.unwrap();
-
-//     client
-//         .on_request::<Initialize, _>(move |_, _| {
-//             Ok(dap::Capabilities {
-//                 supports_step_back: Some(true),
-//                 ..Default::default()
-//             })
-//         })
-//         .await;
-
-//     client.on_request::<Launch, _>(move |_, _| Ok(())).await;
-
-//     let stack_frames = vec![dap::StackFrame {
-//         id: 1,
-//         name: "Stack Frame 1".into(),
-//         source: Some(dap::Source {
-//             name: Some("test.js".into()),
-//             path: Some("/project/src/test.js".into()),
-//             source_reference: None,
-//             presentation_hint: None,
-//             origin: None,
-//             sources: None,
-//             adapter_data: None,
-//             checksums: None,
-//         }),
-//         line: 1,
-//         column: 1,
-//         end_line: None,
-//         end_column: None,
-//         can_restart: None,
-//         instruction_pointer_reference: None,
-//         module_id: None,
-//         presentation_hint: None,
-//     }];
-
-//     let scopes = vec![Scope {
-//         name: "Scope 1".into(),
-//         presentation_hint: None,
-//         variables_reference: 1,
-//         named_variables: None,
-//         indexed_variables: None,
-//         expensive: false,
-//         source: None,
-//         line: None,
-//         column: None,
-//         end_line: None,
-//         end_column: None,
-//     }];
-
-//     let variable_1 = Variable {
-//         name: "variable 1".into(),
-//         value: "1".into(),
-//         type_: None,
-//         presentation_hint: None,
-//         evaluate_name: None,
-//         variables_reference: 2,
-//         named_variables: None,
-//         indexed_variables: None,
-//         memory_reference: None,
-//     };
-
-//     let variable_2 = Variable {
-//         name: "variable 2".into(),
-//         value: "2".into(),
-//         type_: None,
-//         presentation_hint: None,
-//         evaluate_name: None,
-//         variables_reference: 3,
-//         named_variables: None,
-//         indexed_variables: None,
-//         memory_reference: None,
-//     };
-
-//     let variable_3 = Variable {
-//         name: "variable 3".into(),
-//         value: "hello world".into(),
-//         type_: None,
-//         presentation_hint: None,
-//         evaluate_name: None,
-//         variables_reference: 4,
-//         named_variables: None,
-//         indexed_variables: None,
-//         memory_reference: None,
-//     };
-
-//     let variable_4 = Variable {
-//         name: "variable 4".into(),
-//         value: "hello world this is the final variable".into(),
-//         type_: None,
-//         presentation_hint: None,
-//         evaluate_name: None,
-//         variables_reference: 0,
-//         named_variables: None,
-//         indexed_variables: None,
-//         memory_reference: None,
-//     };
-
-//     client
-//         .on_request::<StackTrace, _>({
-//             let stack_frames = std::sync::Arc::new(stack_frames.clone());
-//             move |_, args| {
-//                 assert_eq!(1, args.thread_id);
-
-//                 Ok(dap::StackTraceResponse {
-//                     stack_frames: (*stack_frames).clone(),
-//                     total_frames: None,
-//                 })
-//             }
-//         })
-//         .await;
-
-//     client
-//         .on_request::<Scopes, _>({
-//             let scopes = Arc::new(scopes.clone());
-//             move |_, args| {
-//                 assert_eq!(1, args.frame_id);
-
-//                 Ok(dap::ScopesResponse {
-//                     scopes: (*scopes).clone(),
-//                 })
-//             }
-//         })
-//         .await;
-
-//     let first_variable_request = vec![variable_1.clone(), variable_2.clone()];
-
-//     client
-//         .on_request::<Variables, _>({
-//             move |_, args| {
-//                 assert_eq!(1, args.variables_reference);
-
-//                 Ok(dap::VariablesResponse {
-//                     variables: first_variable_request.clone(),
-//                 })
-//             }
-//         })
-//         .await;
-
-//     client
-//         .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-//             reason: dap::StoppedEventReason::Pause,
-//             description: None,
-//             thread_id: Some(1),
-//             preserve_focus_hint: None,
-//             text: None,
-//             all_threads_stopped: None,
-//             hit_breakpoint_ids: None,
-//         }))
-//         .await;
-
-//     host_cx.run_until_parked();
-//     remote_cx.run_until_parked();
-
-//     let local_debug_item = host_workspace.update(host_cx, |workspace, cx| {
-//         let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-//         let active_debug_panel_item = debug_panel
-//             .update(cx, |this, cx| this.active_debug_panel_item(cx))
-//             .unwrap();
-
-//         assert_eq!(
-//             1,
-//             debug_panel.update(cx, |this, cx| this.pane().unwrap().read(cx).items_len())
-//         );
-//         assert_eq!(client.id(), active_debug_panel_item.read(cx).client_id());
-//         assert_eq!(1, active_debug_panel_item.read(cx).thread_id());
-//         active_debug_panel_item
-//     });
-
-//     let remote_debug_item = remote_workspace.update(remote_cx, |workspace, cx| {
-//         let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-//         let active_debug_panel_item = debug_panel
-//             .update(cx, |this, cx| this.active_debug_panel_item(cx))
-//             .unwrap();
-
-//         assert_eq!(
-//             1,
-//             debug_panel.update(cx, |this, cx| this.pane().unwrap().read(cx).items_len())
-//         );
-//         assert_eq!(client.id(), active_debug_panel_item.read(cx).client_id());
-//         assert_eq!(1, active_debug_panel_item.read(cx).thread_id());
-//         active_debug_panel_item
-//     });
-
-//     let first_visual_entries = vec!["v Scope 1", "    > variable 1", "    > variable 2"];
-//     let first_variable_containers = vec![
-//         VariableContainer {
-//             container_reference: scopes[0].variables_reference,
-//             variable: variable_1.clone(),
-//             depth: 1,
-//         },
-//         VariableContainer {
-//             container_reference: scopes[0].variables_reference,
-//             variable: variable_2.clone(),
-//             depth: 1,
-//         },
-//     ];
-
-//     local_debug_item
-//         .update(host_cx, |this, _| this.variable_list().clone())
-//         .update(host_cx, |variable_list, cx| {
-//             assert_eq!(1, variable_list.scopes().len());
-//             assert_eq!(scopes, variable_list.scopes().get(&1).unwrap().clone());
-//             assert_eq!(&first_variable_containers, &variable_list.variables());
-
-//             variable_list.assert_visual_entries(first_visual_entries.clone(), cx);
-//         });
-
-//     client
-//         .on_request::<Variables, _>({
-//             let variables = Arc::new(vec![variable_3.clone()]);
-//             move |_, args| {
-//                 assert_eq!(2, args.variables_reference);
-
-//                 Ok(dap::VariablesResponse {
-//                     variables: (*variables).clone(),
-//                 })
-//             }
-//         })
-//         .await;
-
-//     remote_debug_item
-//         .update(remote_cx, |this, _| this.variable_list().clone())
-//         .update(remote_cx, |variable_list, cx| {
-//             assert_eq!(1, variable_list.scopes().len());
-//             assert_eq!(scopes, variable_list.scopes().get(&1).unwrap().clone());
-//             assert_eq!(&first_variable_containers, &variable_list.variables());
-
-//             variable_list.assert_visual_entries(first_visual_entries.clone(), cx);
-
-//             variable_list.toggle_variable(&scopes[0], &variable_1, 1, cx);
-//         });
-
-//     host_cx.run_until_parked();
-//     remote_cx.run_until_parked();
-
-//     let second_req_variable_list = vec![
-//         VariableContainer {
-//             container_reference: scopes[0].variables_reference,
-//             variable: variable_1.clone(),
-//             depth: 1,
-//         },
-//         VariableContainer {
-//             container_reference: variable_1.variables_reference,
-//             variable: variable_3.clone(),
-//             depth: 2,
-//         },
-//         VariableContainer {
-//             container_reference: scopes[0].variables_reference,
-//             variable: variable_2.clone(),
-//             depth: 1,
-//         },
-//     ];
-
-//     remote_debug_item
-//         .update(remote_cx, |this, _| this.variable_list().clone())
-//         .update(remote_cx, |variable_list, cx| {
-//             assert_eq!(1, variable_list.scopes().len());
-//             assert_eq!(3, variable_list.variables().len());
-//             assert_eq!(scopes, variable_list.scopes().get(&1).unwrap().clone());
-//             assert_eq!(&second_req_variable_list, &variable_list.variables());
-
-//             variable_list.assert_visual_entries(
-//                 vec![
-//                     "v Scope 1",
-//                     "    v variable 1",
-//                     "        > variable 3",
-//                     "    > variable 2",
-//                 ],
-//                 cx,
-//             );
-//         });
-
-//     client
-//         .on_request::<Variables, _>({
-//             let variables = Arc::new(vec![variable_4.clone()]);
-//             move |_, args| {
-//                 assert_eq!(3, args.variables_reference);
-
-//                 Ok(dap::VariablesResponse {
-//                     variables: (*variables).clone(),
-//                 })
-//             }
-//         })
-//         .await;
-
-//     local_debug_item
-//         .update(host_cx, |this, _| this.variable_list().clone())
-//         .update(host_cx, |variable_list, cx| {
-//             assert_eq!(1, variable_list.scopes().len());
-//             assert_eq!(3, variable_list.variables().len());
-//             assert_eq!(scopes, variable_list.scopes().get(&1).unwrap().clone());
-//             assert_eq!(&second_req_variable_list, &variable_list.variables());
-
-//             variable_list.assert_visual_entries(first_visual_entries.clone(), cx);
-
-//             variable_list.toggle_variable(&scopes[0], &variable_2.clone(), 1, cx);
-//         });
-
-//     host_cx.run_until_parked();
-//     remote_cx.run_until_parked();
-
-//     let final_variable_containers: Vec<VariableContainer> = vec![
-//         VariableContainer {
-//             container_reference: scopes[0].variables_reference,
-//             variable: variable_1.clone(),
-//             depth: 1,
-//         },
-//         VariableContainer {
-//             container_reference: variable_1.variables_reference,
-//             variable: variable_3.clone(),
-//             depth: 2,
-//         },
-//         VariableContainer {
-//             container_reference: scopes[0].variables_reference,
-//             variable: variable_2.clone(),
-//             depth: 1,
-//         },
-//         VariableContainer {
-//             container_reference: variable_2.variables_reference,
-//             variable: variable_4.clone(),
-//             depth: 2,
-//         },
-//     ];
-
-//     remote_debug_item
-//         .update(remote_cx, |this, _| this.variable_list().clone())
-//         .update(remote_cx, |variable_list, cx| {
-//             assert_eq!(1, variable_list.scopes().len());
-//             assert_eq!(4, variable_list.variables().len());
-//             assert_eq!(scopes, variable_list.scopes().get(&1).unwrap().clone());
-//             assert_eq!(&final_variable_containers, &variable_list.variables());
-
-//             variable_list.assert_visual_entries(
-//                 vec![
-//                     "v Scope 1",
-//                     "    v variable 1",
-//                     "        > variable 3",
-//                     "    > variable 2",
-//                 ],
-//                 cx,
-//             );
-//         });
-
-//     local_debug_item
-//         .update(host_cx, |this, _| this.variable_list().clone())
-//         .update(host_cx, |variable_list, cx| {
-//             assert_eq!(1, variable_list.scopes().len());
-//             assert_eq!(4, variable_list.variables().len());
-//             assert_eq!(scopes, variable_list.scopes().get(&1).unwrap().clone());
-//             assert_eq!(&final_variable_containers, &variable_list.variables());
-
-//             variable_list.assert_visual_entries(
-//                 vec![
-//                     "v Scope 1",
-//                     "    > variable 1",
-//                     "    v variable 2",
-//                     "        > variable 4",
-//                 ],
-//                 cx,
-//             );
-//         });
-
-//     late_join_zed.join_project(host_project_id).await;
-//     let (_late_join_client, late_join_workspace, _late_join_project, late_join_cx) =
-//         late_join_zed.expand().await;
-
-//     late_join_cx.run_until_parked();
-
-//     let last_join_remote_item = late_join_workspace.update(late_join_cx, |workspace, cx| {
-//         let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-//         let active_debug_panel_item = debug_panel
-//             .update(cx, |this, cx| this.active_debug_panel_item(cx))
-//             .unwrap();
-
-//         assert_eq!(
-//             1,
-//             debug_panel.update(cx, |this, cx| this.pane().unwrap().read(cx).items_len())
-//         );
-//         assert_eq!(client.id(), active_debug_panel_item.read(cx).client_id());
-//         assert_eq!(1, active_debug_panel_item.read(cx).thread_id());
-//         active_debug_panel_item
-//     });
-
-//     last_join_remote_item
-//         .update(late_join_cx, |this, _| this.variable_list().clone())
-//         .update(late_join_cx, |variable_list, cx| {
-//             assert_eq!(1, variable_list.scopes().len());
-//             assert_eq!(4, variable_list.variables().len());
-//             assert_eq!(scopes, variable_list.scopes().get(&1).unwrap().clone());
-//             assert_eq!(final_variable_containers, variable_list.variables());
-
-//             variable_list.assert_visual_entries(first_visual_entries, cx);
-//         });
-
-//     let shutdown_client = host_project.update(host_cx, |project, cx| {
-//         project.dap_store().update(cx, |dap_store, cx| {
-//             dap_store.shutdown_session(&session.read(cx).id(), cx)
-//         })
-//     });
-
-//     shutdown_client.await.unwrap();
-// }
-
-#[gpui::test]
-async fn test_ignore_breakpoints(
-    _host_cx: &mut TestAppContext,
-    _remote_cx: &mut TestAppContext,
-    _cx_c: &mut TestAppContext,
-) {
-    unimplemented!("Collab is still being refactored");
-    // let executor = host_cx.executor();
-    // let mut server = TestServer::start(executor).await;
-
-    // let (mut host_zed, mut remote_zed, mut late_join_zed) =
-    //     setup_three_member_test(&mut server, host_cx, remote_cx, cx_c).await;
-
-    // let (host_project_id, worktree_id) = host_zed
-    //     .host_project(Some(json!({"test.txt": "one\ntwo\nthree\nfour\nfive"})))
-    //     .await;
-
-    // remote_zed.join_project(host_project_id).await;
-
-    // let (_client_host, host_workspace, host_project, host_cx) = host_zed.expand().await;
-    // let (_client_remote, remote_workspace, remote_project, remote_cx) = remote_zed.expand().await;
-
-    // let project_path = ProjectPath {
-    //     worktree_id,
-    //     path: Arc::from(Path::new(&"test.txt")),
-    // };
-
-    // let local_editor = host_workspace
-    //     .update_in(host_cx, |workspace, window, cx| {
-    //         workspace.open_path(project_path.clone(), None, true, window, cx)
-    //     })
-    //     .await
-    //     .unwrap()
-    //     .downcast::<Editor>()
-    //     .unwrap();
-
-    // local_editor.update_in(host_cx, |editor, window, cx| {
-    //     editor.move_down(&zed_actions::editor::MoveDown, window, cx);
-    //     editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx); // Line 2
-    //     editor.move_down(&zed_actions::editor::MoveDown, window, cx);
-    //     editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx);
-    //     // Line 3
-    // });
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // let task = host_project.update(host_cx, |project, cx| {
-    //     project.start_debug_session(dap::test_config(None), cx)
-    // });
-
-    // let session = task.await.unwrap();
-    // let client = session.read_with(host_cx, |project, _| project.adapter_client().unwrap());
-    // let client_id = client.id();
-
-    // client
-    //     .on_request::<Initialize, _>(move |_, _| {
-    //         Ok(dap::Capabilities {
-    //             supports_configuration_done_request: Some(true),
-    //             ..Default::default()
-    //         })
-    //     })
-    //     .await;
-
-    // let called_set_breakpoints = Arc::new(AtomicBool::new(false));
-    // client
-    //     .on_request::<SetBreakpoints, _>({
-    //         let called_set_breakpoints = called_set_breakpoints.clone();
-    //         move |_, args| {
-    //             assert_eq!("/project/test.txt", args.source.path.unwrap());
-
-    //             let mut actual_breakpoints = args.breakpoints.unwrap();
-    //             actual_breakpoints.sort_by_key(|b| b.line);
-
-    //             let expected_breakpoints = vec![
-    //                 SourceBreakpoint {
-    //                     line: 2,
-    //                     column: None,
-    //                     condition: None,
-    //                     hit_condition: None,
-    //                     log_message: None,
-    //                     mode: None,
-    //                 },
-    //                 SourceBreakpoint {
-    //                     line: 3,
-    //                     column: None,
-    //                     condition: None,
-    //                     hit_condition: None,
-    //                     log_message: None,
-    //                     mode: None,
-    //                 },
-    //             ];
-
-    //             assert_eq!(actual_breakpoints, expected_breakpoints);
-
-    //             called_set_breakpoints.store(true, Ordering::SeqCst);
-
-    //             Ok(dap::SetBreakpointsResponse {
-    //                 breakpoints: Vec::default(),
-    //             })
-    //         }
-    //     })
-    //     .await;
-
-    // client.on_request::<Launch, _>(move |_, _| Ok(())).await;
-    // client
-    //     .on_request::<StackTrace, _>(move |_, _| {
-    //         Ok(dap::StackTraceResponse {
-    //             stack_frames: Vec::default(),
-    //             total_frames: None,
-    //         })
-    //     })
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Initialized(Some(
-    //         dap::Capabilities {
-    //             supports_configuration_done_request: Some(true),
-    //             ..Default::default()
-    //         },
-    //     )))
-    //     .await;
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // assert!(
-    //     called_set_breakpoints.load(std::sync::atomic::Ordering::SeqCst),
-    //     "SetBreakpoint request must be called when starting debug session"
-    // );
-
-    // client
-    //     .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-    //         reason: dap::StoppedEventReason::Pause,
-    //         description: None,
-    //         thread_id: Some(1),
-    //         preserve_focus_hint: None,
-    //         text: None,
-    //         all_threads_stopped: None,
-    //         hit_breakpoint_ids: None,
-    //     }))
-    //     .await;
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // let remote_debug_item = remote_workspace.update(remote_cx, |workspace, cx| {
-    //     let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-    //     let active_session = debug_panel
-    //         .update(cx, |this, cx| this.active_session(cx))
-    //         .unwrap();
-
-    //     assert_eq!(
-    //         1,
-    //         debug_panel.update(cx, |this, cx| this.pane().unwrap().read(cx).items_len())
-    //     );
-
-    //     let session_id = debug_panel.update(cx, |this, cx| {
-    //         this.dap_store()
-    //             .read(cx)
-    //             .session_by_client_id(client.id())
-    //             .unwrap()
-    //             .read(cx)
-    //             .id()
-    //     });
-
-    //     let breakpoints_ignored = active_session.read(cx).are_breakpoints_ignored(cx);
-
-    //     assert_eq!(session_id, active_session.read(cx).session().read(cx).id());
-    //     assert_eq!(false, breakpoints_ignored);
-    //     assert_eq!(client.id(), active_session.read(cx).client_id());
-    //     assert_eq!(1, active_session.read(cx).thread_id().0);
-    //     active_session
-    // });
-
-    // called_set_breakpoints.store(false, Ordering::SeqCst);
-
-    // client
-    //     .on_request::<SetBreakpoints, _>({
-    //         let called_set_breakpoints = called_set_breakpoints.clone();
-    //         move |_, args| {
-    //             assert_eq!("/project/test.txt", args.source.path.unwrap());
-    //             assert_eq!(args.breakpoints, Some(vec![]));
-
-    //             called_set_breakpoints.store(true, Ordering::SeqCst);
-
-    //             Ok(dap::SetBreakpointsResponse {
-    //                 breakpoints: Vec::default(),
-    //             })
-    //         }
-    //     })
-    //     .await;
-
-    // let local_debug_item = host_workspace.update(host_cx, |workspace, cx| {
-    //     let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-    //     let active_session = debug_panel
-    //         .update(cx, |this, cx| this.active_session(cx))
-    //         .unwrap();
-
-    //     assert_eq!(
-    //         1,
-    //         debug_panel.update(cx, |this, cx| this.pane().unwrap().read(cx).items_len())
-    //     );
-
-    //     assert_eq!(false, active_session.read(cx).are_breakpoints_ignored(cx));
-    //     assert_eq!(client.id(), active_session.read(cx).client_id());
-    //     assert_eq!(1, active_session.read(cx).thread_id().0);
-
-    //     active_session
-    // });
-
-    // local_debug_item.update(host_cx, |item, cx| {
-    //     item.toggle_ignore_breakpoints(cx); // Set to true
-    //     assert_eq!(true, item.are_breakpoints_ignored(cx));
-    // });
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // assert!(
-    //     called_set_breakpoints.load(std::sync::atomic::Ordering::SeqCst),
-    //     "SetBreakpoint request must be called to ignore breakpoints"
-    // );
-
-    // client
-    //     .on_request::<SetBreakpoints, _>({
-    //         let called_set_breakpoints = called_set_breakpoints.clone();
-    //         move |_, _args| {
-    //             called_set_breakpoints.store(true, Ordering::SeqCst);
-
-    //             Ok(dap::SetBreakpointsResponse {
-    //                 breakpoints: Vec::default(),
-    //             })
-    //         }
-    //     })
-    //     .await;
-
-    // let remote_editor = remote_workspace
-    //     .update_in(remote_cx, |workspace, window, cx| {
-    //         workspace.open_path(project_path.clone(), None, true, window, cx)
-    //     })
-    //     .await
-    //     .unwrap()
-    //     .downcast::<Editor>()
-    //     .unwrap();
-
-    // called_set_breakpoints.store(false, std::sync::atomic::Ordering::SeqCst);
-
-    // remote_editor.update_in(remote_cx, |editor, window, cx| {
-    //     // Line 1
-    //     editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx);
-    // });
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // assert!(
-    //     called_set_breakpoints.load(std::sync::atomic::Ordering::SeqCst),
-    //     "SetBreakpoint request be called whenever breakpoints are toggled but with not breakpoints"
-    // );
-
-    // remote_debug_item.update(remote_cx, |debug_panel, cx| {
-    //     let breakpoints_ignored = debug_panel.are_breakpoints_ignored(cx);
-
-    //     assert_eq!(true, breakpoints_ignored);
-    //     assert_eq!(client.id(), debug_panel.client_id());
-    //     assert_eq!(1, debug_panel.thread_id().0);
-    // });
-
-    // client
-    //     .on_request::<SetBreakpoints, _>({
-    //         let called_set_breakpoints = called_set_breakpoints.clone();
-    //         move |_, args| {
-    //             assert_eq!("/project/test.txt", args.source.path.unwrap());
-
-    //             let mut actual_breakpoints = args.breakpoints.unwrap();
-    //             actual_breakpoints.sort_by_key(|b| b.line);
-
-    //             let expected_breakpoints = vec![
-    //                 SourceBreakpoint {
-    //                     line: 1,
-    //                     column: None,
-    //                     condition: None,
-    //                     hit_condition: None,
-    //                     log_message: None,
-    //                     mode: None,
-    //                 },
-    //                 SourceBreakpoint {
-    //                     line: 2,
-    //                     column: None,
-    //                     condition: None,
-    //                     hit_condition: None,
-    //                     log_message: None,
-    //                     mode: None,
-    //                 },
-    //                 SourceBreakpoint {
-    //                     line: 3,
-    //                     column: None,
-    //                     condition: None,
-    //                     hit_condition: None,
-    //                     log_message: None,
-    //                     mode: None,
-    //                 },
-    //             ];
-
-    //             assert_eq!(actual_breakpoints, expected_breakpoints);
-
-    //             called_set_breakpoints.store(true, Ordering::SeqCst);
-
-    //             Ok(dap::SetBreakpointsResponse {
-    //                 breakpoints: Vec::default(),
-    //             })
-    //         }
-    //     })
-    //     .await;
-
-    // late_join_zed.join_project(host_project_id).await;
-    // let (_late_join_client, late_join_workspace, late_join_project, late_join_cx) =
-    //     late_join_zed.expand().await;
-
-    // late_join_cx.run_until_parked();
-
-    // let last_join_remote_item = late_join_workspace.update(late_join_cx, |workspace, cx| {
-    //     let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
-    //     let active_session = debug_panel
-    //         .update(cx, |this, cx| this.active_session(cx))
-    //         .unwrap();
-
-    //     let breakpoints_ignored = active_session.read(cx).are_breakpoints_ignored(cx);
-
-    //     assert_eq!(true, breakpoints_ignored);
-
-    //     assert_eq!(
-    //         1,
-    //         debug_panel.update(cx, |this, cx| this.pane().unwrap().read(cx).items_len())
-    //     );
-    //     assert_eq!(client.id(), active_session.read(cx).client_id());
-    //     assert_eq!(1, active_session.read(cx).thread_id().0);
-    //     active_session
-    // });
-
-    // remote_debug_item.update(remote_cx, |item, cx| {
-    //     item.toggle_ignore_breakpoints(cx);
-    // });
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-    // late_join_cx.run_until_parked();
-
-    // assert!(
-    //     called_set_breakpoints.load(std::sync::atomic::Ordering::SeqCst),
-    //     "SetBreakpoint request should be called to update breakpoints"
-    // );
-
-    // client
-    //     .on_request::<SetBreakpoints, _>({
-    //         let called_set_breakpoints = called_set_breakpoints.clone();
-    //         move |_, args| {
-    //             assert_eq!("/project/test.txt", args.source.path.unwrap());
-    //             assert_eq!(args.breakpoints, Some(vec![]));
-
-    //             called_set_breakpoints.store(true, Ordering::SeqCst);
-
-    //             Ok(dap::SetBreakpointsResponse {
-    //                 breakpoints: Vec::default(),
-    //             })
-    //         }
-    //     })
-    //     .await;
-
-    // local_debug_item.update(host_cx, |debug_panel_item, cx| {
-    //     assert_eq!(
-    //         false,
-    //         debug_panel_item.are_breakpoints_ignored(cx),
-    //         "Remote client set this to false"
-    //     );
-    // });
-
-    // remote_debug_item.update(remote_cx, |debug_panel_item, cx| {
-    //     assert_eq!(
-    //         false,
-    //         debug_panel_item.are_breakpoints_ignored(cx),
-    //         "Remote client set this to false"
-    //     );
-    // });
-
-    // last_join_remote_item.update(late_join_cx, |debug_panel_item, cx| {
-    //     assert_eq!(
-    //         false,
-    //         debug_panel_item.are_breakpoints_ignored(cx),
-    //         "Remote client set this to false"
-    //     );
-    // });
-
-    // let shutdown_client = host_project.update(host_cx, |project, cx| {
-    //     project.dap_store().update(cx, |dap_store, cx| {
-    //         dap_store.shutdown_session(&session.read(cx).id(), cx)
-    //     })
-    // });
-
-    // shutdown_client.await.unwrap();
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // remote_project.update(remote_cx, |project, cx| {
-    //     project.dap_store().update(cx, |dap_store, _cx| {
-    //         let sessions = dap_store.sessions().collect::<Vec<_>>();
-
-    //         assert_eq!(
-    //             None,
-    //             dap_store.session_by_client_id(&client_id),
-    //             "No client_id to session mapping should exist after shutdown"
-    //         );
-    //         assert_eq!(
-    //             0,
-    //             sessions.len(),
-    //             "No sessions should be left after shutdown"
-    //         );
-    //     })
-    // });
-
-    // late_join_project.update(late_join_cx, |project, cx| {
-    //     project.dap_store().update(cx, |dap_store, _cx| {
-    //         let sessions = dap_store.sessions().collect::<Vec<_>>();
-
-    //         assert_eq!(
-    //             None,
-    //             dap_store.session_by_client_id(&client_id),
-    //             "No client_id to session mapping should exist after shutdown"
-    //         );
-    //         assert_eq!(
-    //             0,
-    //             sessions.len(),
-    //             "No sessions should be left after shutdown"
-    //         );
-    //     })
-    // });
-}
-
-#[gpui::test]
-async fn test_debug_panel_console(_host_cx: &mut TestAppContext, _remote_cx: &mut TestAppContext) {
-    unimplemented!("Collab is still being refactored");
-    // let executor = host_cx.executor();
-    // let mut server = TestServer::start(executor).await;
-
-    // let (mut host_zed, mut remote_zed) =
-    //     setup_two_member_test(&mut server, host_cx, remote_cx).await;
-
-    // let (host_project_id, _) = host_zed.host_project(None).await;
-    // remote_zed.join_project(host_project_id).await;
-
-    // let (_client_host, _host_workspace, host_project, host_cx) = host_zed.expand().await;
-    // let (_client_remote, remote_workspace, _remote_project, remote_cx) = remote_zed.expand().await;
-
-    // remote_cx.run_until_parked();
-
-    // let task = host_project.update(host_cx, |project, cx| {
-    //     project.start_debug_session(dap::test_config(None), cx)
-    // });
-
-    // let session = task.await.unwrap();
-    // let client = session.read_with(host_cx, |project, _| project.adapter_client().unwrap());
-
-    // client
-    //     .on_request::<Initialize, _>(move |_, _| {
-    //         Ok(dap::Capabilities {
-    //             supports_step_back: Some(false),
-    //             ..Default::default()
-    //         })
-    //     })
-    //     .await;
-
-    // client.on_request::<Launch, _>(move |_, _| Ok(())).await;
-
-    // client
-    //     .on_request::<StackTrace, _>(move |_, _| {
-    //         Ok(dap::StackTraceResponse {
-    //             stack_frames: Vec::default(),
-    //             total_frames: None,
-    //         })
-    //     })
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
-    //         reason: dap::StoppedEventReason::Pause,
-    //         description: None,
-    //         thread_id: Some(1),
-    //         preserve_focus_hint: None,
-    //         text: None,
-    //         all_threads_stopped: None,
-    //         hit_breakpoint_ids: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Output(dap::OutputEvent {
-    //         category: None,
-    //         output: "First line".to_string(),
-    //         data: None,
-    //         variables_reference: None,
-    //         source: None,
-    //         line: None,
-    //         column: None,
-    //         group: None,
-    //         location_reference: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Output(dap::OutputEvent {
-    //         category: Some(dap::OutputEventCategory::Stdout),
-    //         output: "First group".to_string(),
-    //         data: None,
-    //         variables_reference: None,
-    //         source: None,
-    //         line: None,
-    //         column: None,
-    //         group: Some(dap::OutputEventGroup::Start),
-    //         location_reference: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Output(dap::OutputEvent {
-    //         category: Some(dap::OutputEventCategory::Stdout),
-    //         output: "First item in group 1".to_string(),
-    //         data: None,
-    //         variables_reference: None,
-    //         source: None,
-    //         line: None,
-    //         column: None,
-    //         group: None,
-    //         location_reference: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Output(dap::OutputEvent {
-    //         category: Some(dap::OutputEventCategory::Stdout),
-    //         output: "Second item in group 1".to_string(),
-    //         data: None,
-    //         variables_reference: None,
-    //         source: None,
-    //         line: None,
-    //         column: None,
-    //         group: None,
-    //         location_reference: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Output(dap::OutputEvent {
-    //         category: Some(dap::OutputEventCategory::Stdout),
-    //         output: "Second group".to_string(),
-    //         data: None,
-    //         variables_reference: None,
-    //         source: None,
-    //         line: None,
-    //         column: None,
-    //         group: Some(dap::OutputEventGroup::Start),
-    //         location_reference: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Output(dap::OutputEvent {
-    //         category: Some(dap::OutputEventCategory::Stdout),
-    //         output: "First item in group 2".to_string(),
-    //         data: None,
-    //         variables_reference: None,
-    //         source: None,
-    //         line: None,
-    //         column: None,
-    //         group: None,
-    //         location_reference: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Output(dap::OutputEvent {
-    //         category: Some(dap::OutputEventCategory::Stdout),
-    //         output: "Second item in group 2".to_string(),
-    //         data: None,
-    //         variables_reference: None,
-    //         source: None,
-    //         line: None,
-    //         column: None,
-    //         group: None,
-    //         location_reference: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Output(dap::OutputEvent {
-    //         category: Some(dap::OutputEventCategory::Stdout),
-    //         output: "End group 2".to_string(),
-    //         data: None,
-    //         variables_reference: None,
-    //         source: None,
-    //         line: None,
-    //         column: None,
-    //         group: Some(dap::OutputEventGroup::End),
-    //         location_reference: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Output(dap::OutputEvent {
-    //         category: Some(dap::OutputEventCategory::Stdout),
-    //         output: "Third group".to_string(),
-    //         data: None,
-    //         variables_reference: None,
-    //         source: None,
-    //         line: None,
-    //         column: None,
-    //         group: Some(dap::OutputEventGroup::StartCollapsed),
-    //         location_reference: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Output(dap::OutputEvent {
-    //         category: Some(dap::OutputEventCategory::Stdout),
-    //         output: "First item in group 3".to_string(),
-    //         data: None,
-    //         variables_reference: None,
-    //         source: None,
-    //         line: None,
-    //         column: None,
-    //         group: None,
-    //         location_reference: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Output(dap::OutputEvent {
-    //         category: Some(dap::OutputEventCategory::Stdout),
-    //         output: "Second item in group 3".to_string(),
-    //         data: None,
-    //         variables_reference: None,
-    //         source: None,
-    //         line: None,
-    //         column: None,
-    //         group: None,
-    //         location_reference: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Output(dap::OutputEvent {
-    //         category: Some(dap::OutputEventCategory::Stdout),
-    //         output: "End group 3".to_string(),
-    //         data: None,
-    //         variables_reference: None,
-    //         source: None,
-    //         line: None,
-    //         column: None,
-    //         group: Some(dap::OutputEventGroup::End),
-    //         location_reference: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Output(dap::OutputEvent {
-    //         category: Some(dap::OutputEventCategory::Stdout),
-    //         output: "Third item in group 1".to_string(),
-    //         data: None,
-    //         variables_reference: None,
-    //         source: None,
-    //         line: None,
-    //         column: None,
-    //         group: None,
-    //         location_reference: None,
-    //     }))
-    //     .await;
-
-    // client
-    //     .fake_event(dap::messages::Events::Output(dap::OutputEvent {
-    //         category: Some(dap::OutputEventCategory::Stdout),
-    //         output: "Second item".to_string(),
-    //         data: None,
-    //         variables_reference: None,
-    //         source: None,
-    //         line: None,
-    //         column: None,
-    //         group: Some(dap::OutputEventGroup::End),
-    //         location_reference: None,
-    //     }))
-    //     .await;
-
-    // host_cx.run_until_parked();
-    // remote_cx.run_until_parked();
-
-    // active_session(remote_workspace, remote_cx).update(remote_cx, |session_item, cx| {
-    //     session_item
-    //         .mode()
-    //         .as_running()
-    //         .unwrap()
-    //         .read(cx)
-    //         .console()
-    //         .update(cx, |console, cx| {
-    //             console.editor().update(cx, |editor, cx| {
-    //                 pretty_assertions::assert_eq!(
-    //                     "
-    //                     <First line
-    //                     First group
-    //                         First item in group 1
-    //                         Second item in group 1
-    //                         Second group
-    //                             First item in group 2
-    //                             Second item in group 2
-    //                         End group 2
-    //                     ⋯    End group 3
-    //                         Third item in group 1
-    //                     Second item
-    //                 "
-    //                     .unindent(),
-    //                     editor.display_text(cx)
-    //                 );
-    //             })
-    //         });
-    // });
-
-    // let shutdown_client = host_project.update(host_cx, |project, cx| {
-    //     project.dap_store().update(cx, |dap_store, cx| {
-    //         dap_store.shutdown_session(&session.read(cx).session_id(), cx)
-    //     })
-    // });
-
-    // shutdown_client.await.unwrap();
-}

crates/collab/src/tests/agent_sharing_tests.rs → crates/collab/tests/integration/agent_sharing_tests.rs 🔗

@@ -3,7 +3,7 @@ use gpui::{BackgroundExecutor, TestAppContext};
 use rpc::proto;
 use uuid::Uuid;
 
-use crate::tests::TestServer;
+use crate::TestServer;
 
 #[gpui::test]
 async fn test_share_and_retrieve_thread(

crates/collab/src/tests/channel_buffer_tests.rs → crates/collab/tests/integration/channel_buffer_tests.rs 🔗

@@ -1,10 +1,8 @@
-use crate::{
-    rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT},
-    tests::{TestServer, test_server::open_channel_notes},
-};
+use crate::{TestServer, test_server::open_channel_notes};
 use call::ActiveCall;
 use channel::ACKNOWLEDGE_DEBOUNCE_INTERVAL;
 use client::{Collaborator, ParticipantIndex, UserId};
+use collab::rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT};
 use collab_ui::channel_view::ChannelView;
 use collections::HashMap;
 use editor::{Anchor, Editor, MultiBufferOffset, ToOffset};

crates/collab/src/tests/channel_guest_tests.rs → crates/collab/tests/integration/channel_guest_tests.rs 🔗

@@ -1,11 +1,11 @@
-use crate::{db::ChannelId, tests::TestServer};
+use crate::TestServer;
 use call::ActiveCall;
 use chrono::Utc;
+use collab::db::ChannelId;
 use editor::Editor;
 use gpui::{BackgroundExecutor, TestAppContext};
 use rpc::proto;
 use util::rel_path::rel_path;
-
 #[gpui::test]
 async fn test_channel_guests(
     executor: BackgroundExecutor,

crates/collab/src/tests/channel_tests.rs → crates/collab/tests/integration/channel_tests.rs 🔗

@@ -1,11 +1,11 @@
-use crate::{
-    db::{self, UserId},
-    rpc::RECONNECT_TIMEOUT,
-    tests::{RoomParticipants, TestServer, room_participants},
-};
+use crate::{RoomParticipants, TestServer, room_participants};
 use call::ActiveCall;
 use channel::{ChannelMembership, ChannelStore};
 use client::{ChannelId, User};
+use collab::{
+    db::{self, UserId},
+    rpc::RECONNECT_TIMEOUT,
+};
 use futures::future::try_join_all;
 use gpui::{BackgroundExecutor, Entity, SharedString, TestAppContext};
 use rpc::{

crates/collab/tests/integration/collab_tests.rs 🔗

@@ -0,0 +1,230 @@
+use call::Room;
+use client::ChannelId;
+use gpui::{Entity, TestAppContext};
+
+mod agent_sharing_tests;
+mod channel_buffer_tests;
+mod channel_guest_tests;
+mod channel_tests;
+mod db_tests;
+mod editor_tests;
+mod following_tests;
+mod git_tests;
+mod integration_tests;
+mod notification_tests;
+mod random_channel_buffer_tests;
+mod random_project_collaboration_tests;
+mod randomized_test_helpers;
+mod remote_editing_collaboration_tests;
+mod test_server;
+
+pub use randomized_test_helpers::{
+    RandomizedTest, TestError, UserTestPlan, run_randomized_test, save_randomized_test_plan,
+};
+pub use test_server::{TestClient, TestServer};
+
+#[derive(Debug, Eq, PartialEq)]
+struct RoomParticipants {
+    remote: Vec<String>,
+    pending: Vec<String>,
+}
+
+fn room_participants(room: &Entity<Room>, cx: &mut TestAppContext) -> RoomParticipants {
+    room.read_with(cx, |room, _| {
+        let mut remote = room
+            .remote_participants()
+            .values()
+            .map(|participant| participant.user.github_login.clone().to_string())
+            .collect::<Vec<_>>();
+        let mut pending = room
+            .pending_participants()
+            .iter()
+            .map(|user| user.github_login.clone().to_string())
+            .collect::<Vec<_>>();
+        remote.sort();
+        pending.sort();
+        RoomParticipants { remote, pending }
+    })
+}
+
+fn channel_id(room: &Entity<Room>, cx: &mut TestAppContext) -> Option<ChannelId> {
+    cx.read(|cx| room.read(cx).channel_id())
+}
+
+mod auth_token_tests {
+    use collab::auth::{
+        AccessTokenJson, MAX_ACCESS_TOKENS_TO_STORE, VerifyAccessTokenResult, create_access_token,
+        verify_access_token,
+    };
+    use rand::prelude::*;
+    use scrypt::Scrypt;
+    use scrypt::password_hash::{PasswordHasher, SaltString};
+    use sea_orm::EntityTrait;
+
+    use collab::db::{Database, NewUserParams, UserId, access_token};
+    use collab::*;
+
+    #[gpui::test]
+    async fn test_verify_access_token(cx: &mut gpui::TestAppContext) {
+        let test_db = crate::db_tests::TestDb::sqlite(cx.executor());
+        let db = test_db.db();
+
+        let user = db
+            .create_user(
+                "example@example.com",
+                None,
+                false,
+                NewUserParams {
+                    github_login: "example".into(),
+                    github_user_id: 1,
+                },
+            )
+            .await
+            .unwrap();
+
+        let token = create_access_token(db, user.user_id, None).await.unwrap();
+        assert!(matches!(
+            verify_access_token(&token, user.user_id, db).await.unwrap(),
+            VerifyAccessTokenResult {
+                is_valid: true,
+                impersonator_id: None,
+            }
+        ));
+
+        let old_token = create_previous_access_token(user.user_id, None, db)
+            .await
+            .unwrap();
+
+        let old_token_id = serde_json::from_str::<AccessTokenJson>(&old_token)
+            .unwrap()
+            .id;
+
+        let hash = db
+            .transaction(|tx| async move {
+                Ok(access_token::Entity::find_by_id(old_token_id)
+                    .one(&*tx)
+                    .await?)
+            })
+            .await
+            .unwrap()
+            .unwrap()
+            .hash;
+        assert!(hash.starts_with("$scrypt$"));
+
+        assert!(matches!(
+            verify_access_token(&old_token, user.user_id, db)
+                .await
+                .unwrap(),
+            VerifyAccessTokenResult {
+                is_valid: true,
+                impersonator_id: None,
+            }
+        ));
+
+        let hash = db
+            .transaction(|tx| async move {
+                Ok(access_token::Entity::find_by_id(old_token_id)
+                    .one(&*tx)
+                    .await?)
+            })
+            .await
+            .unwrap()
+            .unwrap()
+            .hash;
+        assert!(hash.starts_with("$sha256$"));
+
+        assert!(matches!(
+            verify_access_token(&old_token, user.user_id, db)
+                .await
+                .unwrap(),
+            VerifyAccessTokenResult {
+                is_valid: true,
+                impersonator_id: None,
+            }
+        ));
+
+        assert!(matches!(
+            verify_access_token(&token, user.user_id, db).await.unwrap(),
+            VerifyAccessTokenResult {
+                is_valid: true,
+                impersonator_id: None,
+            }
+        ));
+    }
+
+    async fn create_previous_access_token(
+        user_id: UserId,
+        impersonated_user_id: Option<UserId>,
+        db: &Database,
+    ) -> Result<String> {
+        let access_token = collab::auth::random_token();
+        let access_token_hash = previous_hash_access_token(&access_token)?;
+        let id = db
+            .create_access_token(
+                user_id,
+                impersonated_user_id,
+                &access_token_hash,
+                MAX_ACCESS_TOKENS_TO_STORE,
+            )
+            .await?;
+        Ok(serde_json::to_string(&AccessTokenJson {
+            version: 1,
+            id,
+            token: access_token,
+        })?)
+    }
+
+    #[expect(clippy::result_large_err)]
+    fn previous_hash_access_token(token: &str) -> Result<String> {
+        // Avoid slow hashing in debug mode.
+        let params = if cfg!(debug_assertions) {
+            scrypt::Params::new(1, 1, 1, scrypt::Params::RECOMMENDED_LEN).unwrap()
+        } else {
+            scrypt::Params::new(14, 8, 1, scrypt::Params::RECOMMENDED_LEN).unwrap()
+        };
+
+        Ok(Scrypt
+            .hash_password_customized(
+                token.as_bytes(),
+                None,
+                None,
+                params,
+                &SaltString::generate(PasswordHashRngCompat::new()),
+            )
+            .map_err(anyhow::Error::new)?
+            .to_string())
+    }
+
+    // TODO: remove once we password_hash v0.6 is released.
+    struct PasswordHashRngCompat(rand::rngs::ThreadRng);
+
+    impl PasswordHashRngCompat {
+        fn new() -> Self {
+            Self(rand::rng())
+        }
+    }
+
+    impl scrypt::password_hash::rand_core::RngCore for PasswordHashRngCompat {
+        fn next_u32(&mut self) -> u32 {
+            self.0.next_u32()
+        }
+
+        fn next_u64(&mut self) -> u64 {
+            self.0.next_u64()
+        }
+
+        fn fill_bytes(&mut self, dest: &mut [u8]) {
+            self.0.fill_bytes(dest);
+        }
+
+        fn try_fill_bytes(
+            &mut self,
+            dest: &mut [u8],
+        ) -> Result<(), scrypt::password_hash::rand_core::Error> {
+            self.fill_bytes(dest);
+            Ok(())
+        }
+    }
+
+    impl scrypt::password_hash::rand_core::CryptoRng for PasswordHashRngCompat {}
+}

crates/collab/tests/integration/db_tests.rs 🔗

@@ -0,0 +1,219 @@
+mod buffer_tests;
+mod channel_tests;
+mod db_tests;
+mod extension_tests;
+mod migrations;
+
+use std::sync::Arc;
+use std::sync::atomic::{AtomicI32, Ordering::SeqCst};
+use std::time::Duration;
+
+use collections::HashSet;
+use gpui::BackgroundExecutor;
+use parking_lot::Mutex;
+use rand::prelude::*;
+use sea_orm::ConnectionTrait;
+use sqlx::migrate::MigrateDatabase;
+
+use self::migrations::run_database_migrations;
+
+use collab::db::*;
+
+pub struct TestDb {
+    pub db: Option<Arc<Database>>,
+    pub connection: Option<sqlx::AnyConnection>,
+}
+
+impl TestDb {
+    pub fn sqlite(executor: BackgroundExecutor) -> Self {
+        let url = "sqlite::memory:";
+        let runtime = tokio::runtime::Builder::new_current_thread()
+            .enable_io()
+            .enable_time()
+            .build()
+            .unwrap();
+
+        let mut db = runtime.block_on(async {
+            let mut options = ConnectOptions::new(url);
+            options.max_connections(5);
+            let mut db = Database::new(options).await.unwrap();
+            let sql = include_str!(concat!(
+                env!("CARGO_MANIFEST_DIR"),
+                "/migrations.sqlite/20221109000000_test_schema.sql"
+            ));
+            db.pool
+                .execute(sea_orm::Statement::from_string(
+                    db.pool.get_database_backend(),
+                    sql,
+                ))
+                .await
+                .unwrap();
+            db.initialize_notification_kinds().await.unwrap();
+            db
+        });
+
+        db.test_options = Some(DatabaseTestOptions {
+            executor,
+            runtime,
+            query_failure_probability: parking_lot::Mutex::new(0.0),
+        });
+
+        Self {
+            db: Some(Arc::new(db)),
+            connection: None,
+        }
+    }
+
+    pub fn postgres(executor: BackgroundExecutor) -> Self {
+        static LOCK: Mutex<()> = Mutex::new(());
+
+        let _guard = LOCK.lock();
+        let mut rng = StdRng::from_os_rng();
+        let url = format!(
+            "postgres://postgres@localhost/zed-test-{}",
+            rng.random::<u128>()
+        );
+        let runtime = tokio::runtime::Builder::new_current_thread()
+            .enable_io()
+            .enable_time()
+            .build()
+            .unwrap();
+
+        let mut db = runtime.block_on(async {
+            sqlx::Postgres::create_database(&url)
+                .await
+                .expect("failed to create test db");
+            let mut options = ConnectOptions::new(url);
+            options
+                .max_connections(5)
+                .idle_timeout(Duration::from_secs(0));
+            let mut db = Database::new(options).await.unwrap();
+            let migrations_path = concat!(env!("CARGO_MANIFEST_DIR"), "/migrations");
+            run_database_migrations(db.options(), migrations_path)
+                .await
+                .unwrap();
+            db.initialize_notification_kinds().await.unwrap();
+            db
+        });
+
+        db.test_options = Some(DatabaseTestOptions {
+            executor,
+            runtime,
+            query_failure_probability: parking_lot::Mutex::new(0.0),
+        });
+
+        Self {
+            db: Some(Arc::new(db)),
+            connection: None,
+        }
+    }
+
+    pub fn db(&self) -> &Arc<Database> {
+        self.db.as_ref().unwrap()
+    }
+
+    pub fn set_query_failure_probability(&self, probability: f64) {
+        let database = self.db.as_ref().unwrap();
+        let test_options = database.test_options.as_ref().unwrap();
+        *test_options.query_failure_probability.lock() = probability;
+    }
+}
+
+#[macro_export]
+macro_rules! test_both_dbs {
+    ($test_name:ident, $postgres_test_name:ident, $sqlite_test_name:ident) => {
+        #[cfg(target_os = "macos")]
+        #[gpui::test]
+        async fn $postgres_test_name(cx: &mut gpui::TestAppContext) {
+            let test_db = $crate::db_tests::TestDb::postgres(cx.executor().clone());
+            $test_name(test_db.db()).await;
+        }
+
+        #[gpui::test]
+        async fn $sqlite_test_name(cx: &mut gpui::TestAppContext) {
+            let test_db = $crate::db_tests::TestDb::sqlite(cx.executor().clone());
+            $test_name(test_db.db()).await;
+        }
+    };
+}
+
+impl Drop for TestDb {
+    fn drop(&mut self) {
+        let db = self.db.take().unwrap();
+        if let sea_orm::DatabaseBackend::Postgres = db.pool.get_database_backend() {
+            db.test_options.as_ref().unwrap().runtime.block_on(async {
+                use util::ResultExt;
+                let query = "
+                        SELECT pg_terminate_backend(pg_stat_activity.pid)
+                        FROM pg_stat_activity
+                        WHERE
+                            pg_stat_activity.datname = current_database() AND
+                            pid <> pg_backend_pid();
+                    ";
+                db.pool
+                    .execute(sea_orm::Statement::from_string(
+                        db.pool.get_database_backend(),
+                        query,
+                    ))
+                    .await
+                    .log_err();
+                sqlx::Postgres::drop_database(db.options.get_url())
+                    .await
+                    .log_err();
+            })
+        }
+    }
+}
+
+#[track_caller]
+fn assert_channel_tree_matches(actual: Vec<Channel>, expected: Vec<Channel>) {
+    let expected_channels = expected.into_iter().collect::<HashSet<_>>();
+    let actual_channels = actual.into_iter().collect::<HashSet<_>>();
+    pretty_assertions::assert_eq!(expected_channels, actual_channels);
+}
+
+fn channel_tree(channels: &[(ChannelId, &[ChannelId], &'static str)]) -> Vec<Channel> {
+    use std::collections::HashMap;
+
+    let mut result = Vec::new();
+    let mut order_by_parent: HashMap<Vec<ChannelId>, i32> = HashMap::new();
+
+    for (id, parent_path, name) in channels {
+        let parent_key = parent_path.to_vec();
+        let order = if parent_key.is_empty() {
+            1
+        } else {
+            *order_by_parent
+                .entry(parent_key.clone())
+                .and_modify(|e| *e += 1)
+                .or_insert(1)
+        };
+
+        result.push(Channel {
+            id: *id,
+            name: (*name).to_owned(),
+            visibility: ChannelVisibility::Members,
+            parent_path: parent_key,
+            channel_order: order,
+        });
+    }
+
+    result
+}
+
+static GITHUB_USER_ID: AtomicI32 = AtomicI32::new(5);
+
+async fn new_test_user(db: &Arc<Database>, email: &str) -> UserId {
+    db.create_user(
+        email,
+        None,
+        false,
+        NewUserParams {
+            github_login: email[0..email.find('@').unwrap()].to_string(),
+            github_user_id: GITHUB_USER_ID.fetch_add(1, SeqCst),
+        },
+    )
+    .await
+    .unwrap()
+    .user_id
+}

crates/collab/src/db/tests/channel_tests.rs → crates/collab/tests/integration/db_tests/channel_tests.rs 🔗

@@ -1,10 +1,6 @@
-use crate::{
-    db::{
-        Channel, ChannelId, ChannelRole, Database, NewUserParams, RoomId, UserId,
-        tests::{assert_channel_tree_matches, channel_tree, new_test_user},
-    },
-    test_both_dbs,
-};
+use super::{assert_channel_tree_matches, channel_tree, new_test_user};
+use crate::test_both_dbs;
+use collab::db::{Channel, ChannelId, ChannelRole, Database, NewUserParams, RoomId, UserId};
 use rpc::{
     ConnectionId,
     proto::{self, reorder_channel},
@@ -689,12 +685,12 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
         .await
         .unwrap();
 
-    db.set_channel_visibility(zed_channel, crate::db::ChannelVisibility::Public, admin)
+    db.set_channel_visibility(zed_channel, collab::db::ChannelVisibility::Public, admin)
         .await
         .unwrap();
     db.set_channel_visibility(
         public_channel_id,
-        crate::db::ChannelVisibility::Public,
+        collab::db::ChannelVisibility::Public,
         admin,
     )
     .await

crates/collab/src/db/tests/db_tests.rs → crates/collab/tests/integration/db_tests/db_tests.rs 🔗

@@ -1,7 +1,11 @@
-use super::*;
 use crate::test_both_dbs;
+
+use super::*;
 use chrono::Utc;
+use collab::db::RoomId;
+use collab::db::*;
 use pretty_assertions::assert_eq;
+use rpc::ConnectionId;
 use std::sync::Arc;
 
 test_both_dbs!(
@@ -541,7 +545,7 @@ fn test_fuzzy_like_string() {
 #[cfg(target_os = "macos")]
 #[gpui::test]
 async fn test_fuzzy_search_users(cx: &mut gpui::TestAppContext) {
-    let test_db = tests::TestDb::postgres(cx.executor());
+    let test_db = TestDb::postgres(cx.executor());
     let db = test_db.db();
     for (i, github_login) in [
         "California",
@@ -594,7 +598,7 @@ test_both_dbs!(
 );
 
 async fn test_upsert_shared_thread(db: &Arc<Database>) {
-    use crate::db::SharedThreadId;
+    use collab::db::SharedThreadId;
     use uuid::Uuid;
 
     let user_id = new_test_user(db, "user1@example.com").await;
@@ -624,7 +628,7 @@ test_both_dbs!(
 );
 
 async fn test_upsert_shared_thread_updates_existing(db: &Arc<Database>) {
-    use crate::db::SharedThreadId;
+    use collab::db::SharedThreadId;
     use uuid::Uuid;
 
     let user_id = new_test_user(db, "user1@example.com").await;
@@ -665,7 +669,7 @@ test_both_dbs!(
 );
 
 async fn test_cannot_update_another_users_shared_thread(db: &Arc<Database>) {
-    use crate::db::SharedThreadId;
+    use collab::db::SharedThreadId;
     use uuid::Uuid;
 
     let user1_id = new_test_user(db, "user1@example.com").await;
@@ -694,7 +698,7 @@ test_both_dbs!(
 );
 
 async fn test_get_nonexistent_shared_thread(db: &Arc<Database>) {
-    use crate::db::SharedThreadId;
+    use collab::db::SharedThreadId;
     use uuid::Uuid;
 
     let result = db

crates/collab/src/db/tests/extension_tests.rs → crates/collab/tests/integration/db_tests/extension_tests.rs 🔗

@@ -3,13 +3,11 @@ use std::sync::Arc;
 
 use rpc::ExtensionProvides;
 
-use super::Database;
-use crate::db::ExtensionVersionConstraints;
-use crate::{
-    db::{ExtensionMetadata, NewExtensionVersion, queries::extensions::convert_time_to_chrono},
-    test_both_dbs,
-};
-
+use crate::test_both_dbs;
+use collab::db::Database;
+use collab::db::ExtensionVersionConstraints;
+use collab::db::{NewExtensionVersion, queries::extensions::convert_time_to_chrono};
+use rpc::ExtensionMetadata;
 test_both_dbs!(
     test_extensions,
     test_extensions_postgres,

crates/collab/src/tests/editor_tests.rs → crates/collab/tests/integration/editor_tests.rs 🔗

@@ -1,5 +1,6 @@
-use crate::{rpc::RECONNECT_TIMEOUT, tests::TestServer};
+use crate::TestServer;
 use call::ActiveCall;
+use collab::rpc::RECONNECT_TIMEOUT;
 use collections::{HashMap, HashSet};
 use editor::{
     DocumentColorsRenderMode, Editor, FETCH_COLORS_DEBOUNCE_TIMEOUT, MultiBufferOffset, RowInfo,

crates/collab/src/tests/following_tests.rs → crates/collab/tests/integration/following_tests.rs 🔗

@@ -1,5 +1,5 @@
 #![allow(clippy::reversed_empty_ranges)]
-use crate::tests::TestServer;
+use crate::TestServer;
 use call::{ActiveCall, ParticipantLocation};
 use client::ChannelId;
 use collab_ui::{
@@ -462,7 +462,7 @@ async fn test_basic_following(
 
     // #[cfg(all(not(target_os = "macos"), not(target_os = "windows")))]
     {
-        use crate::rpc::RECONNECT_TIMEOUT;
+        use collab::rpc::RECONNECT_TIMEOUT;
         use gpui::TestScreenCaptureSource;
         use workspace::{
             dock::{DockPosition, test::TestPanel},

crates/collab/src/tests/git_tests.rs → crates/collab/tests/integration/git_tests.rs 🔗

@@ -10,7 +10,7 @@ use util::{path, rel_path::rel_path};
 use workspace::Workspace;
 
 //
-use crate::tests::TestServer;
+use crate::TestServer;
 
 #[gpui::test]
 async fn test_project_diff(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {

crates/collab/src/tests/integration_tests.rs → crates/collab/tests/integration/integration_tests.rs 🔗

@@ -1,9 +1,6 @@
 use crate::{
-    rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT},
-    tests::{
-        RoomParticipants, TestClient, TestServer, channel_id, following_tests::join_channel,
-        room_participants,
-    },
+    RoomParticipants, TestClient, TestServer, channel_id, following_tests::join_channel,
+    room_participants,
 };
 use anyhow::{Result, anyhow};
 use assistant_slash_command::SlashCommandWorkingSet;
@@ -11,6 +8,7 @@ use assistant_text_thread::TextThreadStore;
 use buffer_diff::{DiffHunkSecondaryStatus, DiffHunkStatus, assert_hunks};
 use call::{ActiveCall, ParticipantLocation, Room, room};
 use client::{RECEIVE_TIMEOUT, User};
+use collab::rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT};
 use collections::{BTreeMap, HashMap, HashSet};
 use fs::{FakeFs, Fs as _, RemoveOptions};
 use futures::{StreamExt as _, channel::mpsc};

crates/collab/src/tests/notification_tests.rs → crates/collab/tests/integration/notification_tests.rs 🔗

@@ -6,7 +6,7 @@ use parking_lot::Mutex;
 use pretty_assertions::assert_eq;
 use rpc::{Notification, proto};
 
-use crate::tests::TestServer;
+use crate::TestServer;
 
 #[gpui::test]
 async fn test_notifications(

crates/collab/src/tests/random_channel_buffer_tests.rs → crates/collab/tests/integration/random_channel_buffer_tests.rs 🔗

@@ -1,4 +1,4 @@
-use crate::db::ChannelRole;
+use collab::db::*;
 
 use super::{RandomizedTest, TestClient, TestError, TestServer, UserTestPlan, run_randomized_test};
 use anyhow::Result;
@@ -13,10 +13,7 @@ use std::{
 };
 use text::Bias;
 
-#[gpui::test(
-    iterations = 100,
-    on_failure = "crate::tests::save_randomized_test_plan"
-)]
+#[gpui::test(iterations = 100, on_failure = "crate::save_randomized_test_plan")]
 async fn test_random_channel_buffers(
     cx: &mut TestAppContext,
     executor: BackgroundExecutor,

crates/collab/src/tests/random_project_collaboration_tests.rs → crates/collab/tests/integration/random_project_collaboration_tests.rs 🔗

@@ -1,8 +1,8 @@
-use super::{RandomizedTest, TestClient, TestError, TestServer, UserTestPlan};
-use crate::{db::UserId, tests::run_randomized_test};
+use crate::{RandomizedTest, TestClient, TestError, TestServer, UserTestPlan, run_randomized_test};
 use anyhow::{Context as _, Result};
 use async_trait::async_trait;
 use call::ActiveCall;
+use collab::db::UserId;
 use collections::{BTreeMap, HashMap};
 use editor::Bias;
 use fs::{FakeFs, Fs as _};
@@ -33,10 +33,7 @@ use util::{
     rel_path::{RelPath, RelPathBuf, rel_path},
 };
 
-#[gpui::test(
-    iterations = 100,
-    on_failure = "crate::tests::save_randomized_test_plan"
-)]
+#[gpui::test(iterations = 100, on_failure = "crate::save_randomized_test_plan")]
 async fn test_random_project_collaboration(
     cx: &mut TestAppContext,
     executor: BackgroundExecutor,

crates/collab/src/tests/randomized_test_helpers.rs → crates/collab/tests/integration/randomized_test_helpers.rs 🔗

@@ -1,9 +1,9 @@
-use crate::{
+use crate::{TestClient, TestServer};
+use async_trait::async_trait;
+use collab::{
     db::{self, NewUserParams, UserId},
     rpc::{CLEANUP_TIMEOUT, RECONNECT_TIMEOUT},
-    tests::{TestClient, TestServer},
 };
-use async_trait::async_trait;
 use futures::StreamExt;
 use gpui::{BackgroundExecutor, Task, TestAppContext};
 use parking_lot::Mutex;

crates/collab/src/tests/test_server.rs → crates/collab/tests/integration/test_server.rs 🔗

@@ -1,9 +1,3 @@
-use crate::{
-    AppState, Config,
-    db::{NewUserParams, UserId, tests::TestDb},
-    executor::Executor,
-    rpc::{CLEANUP_TIMEOUT, Principal, RECONNECT_TIMEOUT, Server, ZedVersion},
-};
 use anyhow::anyhow;
 use call::ActiveCall;
 use channel::{ChannelBuffer, ChannelStore};
@@ -13,6 +7,12 @@ use client::{
     proto::PeerId,
 };
 use clock::FakeSystemClock;
+use collab::{
+    AppState, Config,
+    db::{NewUserParams, UserId},
+    executor::Executor,
+    rpc::{CLEANUP_TIMEOUT, Principal, RECONNECT_TIMEOUT, Server, ZedVersion},
+};
 use collab_ui::channel_view::ChannelView;
 use collections::{HashMap, HashSet};
 
@@ -49,6 +49,8 @@ use workspace::{Workspace, WorkspaceStore};
 
 use livekit_client::test::TestServer as LivekitTestServer;
 
+use crate::db_tests::TestDb;
+
 pub struct TestServer {
     pub app_state: Arc<AppState>,
     pub test_livekit_server: Arc<LivekitTestServer>,