diff --git a/Cargo.lock b/Cargo.lock index 11c9d229c8770858cf520429321327befd5d79e9..1e9d43b2c0bbba211de24489453d31d271dca372 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5850,6 +5850,7 @@ dependencies = [ "tide-compress", "time 0.2.25", "toml", + "util", "zed", ] diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index f4622d1f6e1a7d6bc34d724a4b5f704a144a11ac..67622db83f94539ff40ec728843799a51628e8aa 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -1,6 +1,7 @@ #[cfg(test)] #[ctor::ctor] fn init_logger() { - // std::env::set_var("RUST_LOG", "info"); - env_logger::init(); + if std::env::var("RUST_LOG").is_ok() { + env_logger::init(); + } } diff --git a/crates/gpui/src/test.rs b/crates/gpui/src/test.rs index b4b4e621ac313828da5c8a9185d43a501986bd30..af6430d36c7901dbea1a273996cd1f6536ef365b 100644 --- a/crates/gpui/src/test.rs +++ b/crates/gpui/src/test.rs @@ -18,9 +18,9 @@ use crate::{ #[cfg(test)] #[ctor::ctor] fn init_logger() { - env_logger::builder() - .filter_level(log::LevelFilter::Info) - .init(); + if std::env::var("RUST_LOG").is_ok() { + env_logger::init(); + } } pub fn run_test( diff --git a/crates/language/src/tests.rs b/crates/language/src/tests.rs index 25f58cdb420ea766c123cef9475af0fc5d406547..1cede148dec87f9b278e09079c6ccd592614e1f4 100644 --- a/crates/language/src/tests.rs +++ b/crates/language/src/tests.rs @@ -17,8 +17,9 @@ use util::test::Network; #[cfg(test)] #[ctor::ctor] fn init_logger() { - // std::env::set_var("RUST_LOG", "info"); - env_logger::init(); + if std::env::var("RUST_LOG").is_ok() { + env_logger::init(); + } } #[test] diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index f0bf4b47cb25d5bf0e0efd2bace07e5bb97c5dd6..e9f9afd3fbcf892beba6dddbcb78648b80ffd7d0 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -562,7 +562,7 @@ impl FakeLanguageServer { request.params, ); } else { - println!( + log::info!( "skipping message in fake language server {:?}", std::str::from_utf8(&self.buffer) ); diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml index 2ce5a6342daf0e6dbea0bf6e7183ce198de6f530..a4357415da4c9014fe5aad6f6ba19181b178c587 100644 --- a/crates/server/Cargo.toml +++ b/crates/server/Cargo.toml @@ -61,6 +61,7 @@ gpui = { path = "../gpui" } zed = { path = "../zed", features = ["test-support"] } ctor = "0.1" env_logger = "0.8" +util = { path = "../util" } lazy_static = "1.4" serde_json = { version = "1.0.64", features = ["preserve_order"] } diff --git a/crates/server/src/db.rs b/crates/server/src/db.rs index f71f40efd08aae42b5f869b13bf8fbae77ee7cd6..a48673f30fbeb70ff47f9fbab2b4b58aef23fbba 100644 --- a/crates/server/src/db.rs +++ b/crates/server/src/db.rs @@ -526,68 +526,120 @@ pub struct ChannelMessage { #[cfg(test)] pub mod tests { use super::*; + use lazy_static::lazy_static; + use parking_lot::Mutex; use rand::prelude::*; use sqlx::{ migrate::{MigrateDatabase, Migrator}, Postgres, }; - use std::path::Path; + use std::{ + mem, + path::Path, + sync::atomic::{AtomicUsize, Ordering::SeqCst}, + }; + use util::ResultExt as _; pub struct TestDb { - pub db: Db, + pub db: Option, pub name: String, pub url: String, } + lazy_static! { + static ref DB_POOL: Mutex> = Default::default(); + static ref DB_COUNT: AtomicUsize = Default::default(); + } + impl TestDb { pub fn new() -> Self { - // Enable tests to run in parallel by serializing the creation of each test database. - lazy_static::lazy_static! { - static ref DB_CREATION: std::sync::Mutex<()> = std::sync::Mutex::new(()); - } - - let mut rng = StdRng::from_entropy(); - let name = format!("zed-test-{}", rng.gen::()); - let url = format!("postgres://postgres@localhost/{}", name); - let migrations_path = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/migrations")); - let db = block_on(async { - { - let _lock = DB_CREATION.lock(); + DB_COUNT.fetch_add(1, SeqCst); + let mut pool = DB_POOL.lock(); + if let Some(db) = pool.pop() { + db.truncate(); + db + } else { + let mut rng = StdRng::from_entropy(); + let name = format!("zed-test-{}", rng.gen::()); + let url = format!("postgres://postgres@localhost/{}", name); + let migrations_path = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/migrations")); + let db = block_on(async { Postgres::create_database(&url) .await .expect("failed to create test db"); + let mut db = Db::new(&url, 5).await.unwrap(); + db.test_mode = true; + let migrator = Migrator::new(migrations_path).await.unwrap(); + migrator.run(&db.pool).await.unwrap(); + db + }); + + Self { + db: Some(db), + name, + url, } - let mut db = Db::new(&url, 5).await.unwrap(); - db.test_mode = true; - let migrator = Migrator::new(migrations_path).await.unwrap(); - migrator.run(&db.pool).await.unwrap(); - db - }); - - Self { db, name, url } + } } pub fn db(&self) -> &Db { - &self.db + self.db.as_ref().unwrap() } - } - impl Drop for TestDb { - fn drop(&mut self) { + fn truncate(&self) { block_on(async { let query = " - SELECT pg_terminate_backend(pg_stat_activity.pid) - FROM pg_stat_activity - WHERE pg_stat_activity.datname = '{}' AND pid <> pg_backend_pid(); + SELECT tablename FROM pg_tables + WHERE schemaname = 'public'; "; - sqlx::query(query) - .bind(&self.name) - .execute(&self.db.pool) + let table_names = sqlx::query_scalar::<_, String>(query) + .fetch_all(&self.db().pool) .await .unwrap(); - self.db.pool.close().await; - Postgres::drop_database(&self.url).await.unwrap(); - }); + sqlx::query(&format!( + "TRUNCATE TABLE {} RESTART IDENTITY", + table_names.join(", ") + )) + .execute(&self.db().pool) + .await + .unwrap(); + }) + } + + async fn teardown(mut self) -> Result<()> { + let db = self.db.take().unwrap(); + let query = " + SELECT pg_terminate_backend(pg_stat_activity.pid) + FROM pg_stat_activity + WHERE pg_stat_activity.datname = '{}' AND pid <> pg_backend_pid(); + "; + sqlx::query(query) + .bind(&self.name) + .execute(&db.pool) + .await?; + db.pool.close().await; + Postgres::drop_database(&self.url).await?; + Ok(()) + } + } + + impl Drop for TestDb { + fn drop(&mut self) { + if let Some(db) = self.db.take() { + DB_POOL.lock().push(TestDb { + db: Some(db), + name: mem::take(&mut self.name), + url: mem::take(&mut self.url), + }); + if DB_COUNT.fetch_sub(1, SeqCst) == 1 { + block_on(async move { + let mut pool = DB_POOL.lock(); + for db in pool.drain(..) { + db.teardown().await.log_err(); + } + }); + } + } } } diff --git a/crates/server/src/rpc.rs b/crates/server/src/rpc.rs index ee2fb6a94d9baf767ed17f2c35ddc572cab9deeb..8314bec3db5080dabaca9f46ea0025fffb584960 100644 --- a/crates/server/src/rpc.rs +++ b/crates/server/src/rpc.rs @@ -1244,11 +1244,12 @@ mod tests { #[cfg(test)] #[ctor::ctor] fn init_logger() { - // std::env::set_var("RUST_LOG", "info"); - env_logger::init(); + if std::env::var("RUST_LOG").is_ok() { + env_logger::init(); + } } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_share_project(mut cx_a: TestAppContext, mut cx_b: TestAppContext) { let (window_b, _) = cx_b.add_window(|_| EmptyView); let lang_registry = Arc::new(LanguageRegistry::new()); @@ -1387,7 +1388,7 @@ mod tests { .await; } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_unshare_project(mut cx_a: TestAppContext, mut cx_b: TestAppContext) { let lang_registry = Arc::new(LanguageRegistry::new()); let fs = Arc::new(FakeFs::new(cx_a.background())); @@ -1484,7 +1485,7 @@ mod tests { .unwrap(); } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_propagate_saves_and_fs_changes( mut cx_a: TestAppContext, mut cx_b: TestAppContext, @@ -1674,7 +1675,7 @@ mod tests { .await; } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_buffer_conflict_after_save(mut cx_a: TestAppContext, mut cx_b: TestAppContext) { cx_a.foreground().forbid_parking(); let lang_registry = Arc::new(LanguageRegistry::new()); @@ -1767,7 +1768,7 @@ mod tests { }); } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_buffer_reloading(mut cx_a: TestAppContext, mut cx_b: TestAppContext) { cx_a.foreground().forbid_parking(); let lang_registry = Arc::new(LanguageRegistry::new()); @@ -1929,7 +1930,7 @@ mod tests { buffer_b.condition(&cx_b, |buf, _| buf.text() == text).await; } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_leaving_worktree_while_opening_buffer( mut cx_a: TestAppContext, mut cx_b: TestAppContext, @@ -2007,7 +2008,7 @@ mod tests { .await; } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_peer_disconnection(mut cx_a: TestAppContext, mut cx_b: TestAppContext) { cx_a.foreground().forbid_parking(); let lang_registry = Arc::new(LanguageRegistry::new()); @@ -2078,7 +2079,7 @@ mod tests { .await; } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_collaborating_with_diagnostics( mut cx_a: TestAppContext, mut cx_b: TestAppContext, @@ -2302,7 +2303,7 @@ mod tests { }); } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_collaborating_with_completion( mut cx_a: TestAppContext, mut cx_b: TestAppContext, @@ -2527,7 +2528,7 @@ mod tests { ); } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_formatting_buffer(mut cx_a: TestAppContext, mut cx_b: TestAppContext) { cx_a.foreground().forbid_parking(); let mut lang_registry = Arc::new(LanguageRegistry::new()); @@ -2631,7 +2632,7 @@ mod tests { ); } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_definition(mut cx_a: TestAppContext, mut cx_b: TestAppContext) { cx_a.foreground().forbid_parking(); let mut lang_registry = Arc::new(LanguageRegistry::new()); @@ -2788,7 +2789,7 @@ mod tests { .await; } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_open_buffer_while_getting_definition_pointing_to_it( mut cx_a: TestAppContext, mut cx_b: TestAppContext, @@ -2902,7 +2903,7 @@ mod tests { assert_eq!(definitions[0].target_buffer, buffer_b2); } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_basic_chat(mut cx_a: TestAppContext, mut cx_b: TestAppContext) { cx_a.foreground().forbid_parking(); @@ -3042,7 +3043,7 @@ mod tests { .await; } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_chat_message_validation(mut cx_a: TestAppContext) { cx_a.foreground().forbid_parking(); @@ -3102,7 +3103,7 @@ mod tests { ); } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_chat_reconnection(mut cx_a: TestAppContext, mut cx_b: TestAppContext) { cx_a.foreground().forbid_parking(); @@ -3314,7 +3315,7 @@ mod tests { .await; } - #[gpui::test] + #[gpui::test(iterations = 10)] async fn test_contacts( mut cx_a: TestAppContext, mut cx_b: TestAppContext, diff --git a/crates/text/src/tests.rs b/crates/text/src/tests.rs index e45011c4ac7965af39eec3c8bd7336ec1571288c..ad0cc7aa304e6ece4241126d80a750cb9f7117f0 100644 --- a/crates/text/src/tests.rs +++ b/crates/text/src/tests.rs @@ -12,8 +12,9 @@ use util::test::Network; #[cfg(test)] #[ctor::ctor] fn init_logger() { - // std::env::set_var("RUST_LOG", "info"); - env_logger::init(); + if std::env::var("RUST_LOG").is_ok() { + env_logger::init(); + } } #[test] diff --git a/crates/zed/src/test.rs b/crates/zed/src/test.rs index a365fdc6f46c7632f302c8a6fdbe25919c0edc1e..5bf43b555ff3755185af0c9dab02eb54f6eeee85 100644 --- a/crates/zed/src/test.rs +++ b/crates/zed/src/test.rs @@ -12,7 +12,9 @@ use workspace::Settings; #[cfg(test)] #[ctor::ctor] fn init_logger() { - env_logger::init(); + if std::env::var("RUST_LOG").is_ok() { + env_logger::init(); + } } pub fn test_app_state(cx: &mut MutableAppContext) -> Arc {