diff --git a/lib/testutils/src/lib.rs b/lib/testutils/src/lib.rs index adc878dce5..a074ac1f56 100644 --- a/lib/testutils/src/lib.rs +++ b/lib/testutils/src/lib.rs @@ -61,7 +61,7 @@ use jj_lib::workspace::Workspace; use pollster::FutureExt; use tempfile::TempDir; -use crate::test_backend::TestBackend; +use crate::test_backend::TestBackendFactory; pub mod test_backend; pub mod test_signing_backend; @@ -122,12 +122,15 @@ pub fn user_settings() -> UserSettings { #[derive(Debug)] pub struct TestEnvironment { temp_dir: TempDir, + test_backend_factory: TestBackendFactory, } impl TestEnvironment { pub fn init() -> Self { - let temp_dir = new_temp_dir(); - TestEnvironment { temp_dir } + TestEnvironment { + temp_dir: new_temp_dir(), + test_backend_factory: TestBackendFactory::default(), + } } pub fn root(&self) -> &Path { @@ -136,10 +139,10 @@ impl TestEnvironment { pub fn default_store_factories(&self) -> StoreFactories { let mut factories = StoreFactories::default(); - factories.add_backend( - "test", - Box::new(|_settings, store_path| Ok(Box::new(TestBackend::load(store_path)))), - ); + factories.add_backend("test", { + let factory = self.test_backend_factory.clone(); + Box::new(move |_settings, store_path| Ok(Box::new(factory.load(store_path)))) + }); factories.add_backend( SecretBackend::name(), Box::new(|settings, store_path| { @@ -177,13 +180,14 @@ pub enum TestRepoBackend { impl TestRepoBackend { fn init_backend( &self, + env: &TestEnvironment, settings: &UserSettings, store_path: &Path, ) -> Result, BackendInitError> { match self { TestRepoBackend::Git => Ok(Box::new(GitBackend::init_internal(settings, store_path)?)), TestRepoBackend::Local => Ok(Box::new(LocalBackend::init(store_path))), - TestRepoBackend::Test => Ok(Box::new(TestBackend::init(store_path))), + TestRepoBackend::Test => Ok(Box::new(env.test_backend_factory.init(store_path))), } } } @@ -213,7 +217,7 @@ impl TestRepo { let repo = ReadonlyRepo::init( settings, &repo_dir, - &move |settings, store_path| backend.init_backend(settings, store_path), + &|settings, store_path| backend.init_backend(&env, settings, store_path), Signer::from_settings(settings).unwrap(), ReadonlyRepo::default_op_store_initializer(), ReadonlyRepo::default_op_heads_store_initializer(), @@ -266,7 +270,7 @@ impl TestWorkspace { let (workspace, repo) = Workspace::init_with_backend( settings, &workspace_root, - &move |settings, store_path| backend.init_backend(settings, store_path), + &|settings, store_path| backend.init_backend(&env, settings, store_path), signer, ) .unwrap(); diff --git a/lib/testutils/src/test_backend.rs b/lib/testutils/src/test_backend.rs index 1630cb4db6..fd309af440 100644 --- a/lib/testutils/src/test_backend.rs +++ b/lib/testutils/src/test_backend.rs @@ -24,7 +24,6 @@ use std::path::PathBuf; use std::sync::Arc; use std::sync::Mutex; use std::sync::MutexGuard; -use std::sync::OnceLock; use std::time::SystemTime; use async_trait::async_trait; @@ -54,16 +53,11 @@ use jj_lib::repo_path::RepoPathBuf; const HASH_LENGTH: usize = 10; const CHANGE_ID_LENGTH: usize = 16; -static BACKEND_DATA: OnceLock>>>> = - OnceLock::new(); - // Keyed by canonical store path. Since we just use the path as a key, we can't // rely on on the file system to resolve two different uncanonicalized paths to // the same real path (as we would if we just used the path with `std::fs` // functions). -fn backend_data() -> &'static Mutex>>> { - BACKEND_DATA.get_or_init(|| Mutex::new(HashMap::new())) -} +type TestBackendDataMap = HashMap>>; #[derive(Default)] pub struct TestBackendData { @@ -74,6 +68,39 @@ pub struct TestBackendData { conflicts: HashMap>, } +#[derive(Clone, Default)] +pub struct TestBackendFactory { + backend_data: Arc>, +} + +impl TestBackendFactory { + pub fn init(&self, store_path: &Path) -> TestBackend { + let data = Arc::new(Mutex::new(TestBackendData::default())); + self.backend_data + .lock() + .unwrap() + .insert(store_path.canonicalize().unwrap(), data.clone()); + TestBackend::with_data(data) + } + + pub fn load(&self, store_path: &Path) -> TestBackend { + let data = self + .backend_data + .lock() + .unwrap() + .get(&store_path.canonicalize().unwrap()) + .unwrap() + .clone(); + TestBackend::with_data(data) + } +} + +impl Debug for TestBackendFactory { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + f.debug_struct("TestBackendFactory").finish_non_exhaustive() + } +} + fn get_hash(content: &(impl jj_lib::content_hash::ContentHash + ?Sized)) -> Vec { jj_lib::content_hash::blake2b_hash(content).as_slice()[..HASH_LENGTH].to_vec() } @@ -94,30 +121,7 @@ pub struct TestBackend { } impl TestBackend { - pub fn init(store_path: &Path) -> Self { - let root_commit_id = CommitId::from_bytes(&[0; HASH_LENGTH]); - let root_change_id = ChangeId::from_bytes(&[0; CHANGE_ID_LENGTH]); - let empty_tree_id = TreeId::new(get_hash(&Tree::default())); - let data = Arc::new(Mutex::new(TestBackendData::default())); - backend_data() - .lock() - .unwrap() - .insert(store_path.canonicalize().unwrap(), data.clone()); - TestBackend { - root_commit_id, - root_change_id, - empty_tree_id, - data, - } - } - - pub fn load(store_path: &Path) -> Self { - let data = backend_data() - .lock() - .unwrap() - .get(&store_path.canonicalize().unwrap()) - .unwrap() - .clone(); + pub fn with_data(data: Arc>) -> Self { let root_commit_id = CommitId::from_bytes(&[0; HASH_LENGTH]); let root_change_id = ChangeId::from_bytes(&[0; CHANGE_ID_LENGTH]); let empty_tree_id = TreeId::new(get_hash(&Tree::default()));