From b1e60b37ea73a2f2cb022d4448e16d44a65dcbf7 Mon Sep 17 00:00:00 2001 From: Martin von Zweigbergk Date: Sun, 1 Aug 2021 21:47:34 -0700 Subject: [PATCH] view: add tests of merging views I'm about to add some support for branches and tags (for issue #21) and it seems that we didn't have explicit testing of merging of views. There was `test_import_refs_merge()` in `test_git.rs` but that's specifically for git refs. It seems that it's made obsolete by the tests added by this commit, so I'm removing it. --- lib/tests/test_git.rs | 49 ------------- lib/tests/test_view.rs | 156 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 155 insertions(+), 50 deletions(-) diff --git a/lib/tests/test_git.rs b/lib/tests/test_git.rs index 70d49dfa2..2a9aefc57 100644 --- a/lib/tests/test_git.rs +++ b/lib/tests/test_git.rs @@ -145,59 +145,10 @@ fn test_import_refs_reimport() { ); } -fn git_ref(git_repo: &git2::Repository, name: &str, target: Oid) { - git_repo.reference(name, target, true, "").unwrap(); -} - fn delete_git_ref(git_repo: &git2::Repository, name: &str) { git_repo.find_reference(name).unwrap().delete().unwrap(); } -#[test] -fn test_import_refs_merge() { - let settings = testutils::user_settings(); - let (_temp_dir, repo) = testutils::init_repo(&settings, true); - let git_repo = repo.store().git_repo().unwrap(); - - let commit1 = empty_git_commit(&git_repo, "refs/heads/main", &[]); - let commit2 = empty_git_commit(&git_repo, "refs/heads/main", &[&commit1]); - let commit3 = empty_git_commit(&git_repo, "refs/heads/main", &[&commit2]); - let commit4 = empty_git_commit(&git_repo, "refs/heads/feature1", &[&commit2]); - let commit5 = empty_git_commit(&git_repo, "refs/heads/feature2", &[&commit2]); - let mut tx = repo.start_transaction("initial import"); - jujutsu_lib::git::import_refs(tx.mut_repo(), &git_repo).unwrap(); - let repo = tx.commit(); - - // One of the concurrent operations: - delete_git_ref(&git_repo, "refs/heads/feature1"); - git_ref(&git_repo, "refs/heads/main", commit4.id()); - let mut tx1 = repo.start_transaction("concurrent import 1"); - jujutsu_lib::git::import_refs(tx1.mut_repo(), &git_repo).unwrap(); - tx1.commit(); - - // The other concurrent operation: - let mut tx2 = repo.start_transaction("concurrent import 2"); - git_ref(&git_repo, "refs/heads/main", commit5.id()); - jujutsu_lib::git::import_refs(tx2.mut_repo(), &git_repo).unwrap(); - tx2.commit(); - - // Reload the repo, causing the operations to be merged. - let repo = repo.reload(); - - let view = repo.view(); - let git_refs = view.git_refs(); - assert_eq!(git_refs.len(), 2); - assert_eq!( - git_refs.get("refs/heads/main"), - Some(RefTarget::Conflict { - removes: vec![commit_id(&commit3)], - adds: vec![commit_id(&commit4), commit_id(&commit5)] - }) - .as_ref() - ); - assert_eq!(git_refs.get("refs/heads/feature1"), None); -} - #[test] fn test_import_refs_empty_git_repo() { let settings = testutils::user_settings(); diff --git a/lib/tests/test_view.rs b/lib/tests/test_view.rs index de2a8eb69..c3e37513a 100644 --- a/lib/tests/test_view.rs +++ b/lib/tests/test_view.rs @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use jujutsu_lib::op_store::RefTarget; use jujutsu_lib::testutils; use jujutsu_lib::testutils::CommitGraphBuilder; -use maplit::hashset; +use maplit::{btreemap, hashset}; use test_case::test_case; #[test_case(false ; "local store")] @@ -75,3 +76,156 @@ fn test_heads_merge(use_git: bool) { hashset! {wc.current_commit_id(), merge.id().clone()} ); } + +#[test] +fn test_merge_views_heads() { + // Tests merging of the view's heads (by performing concurrent operations). + let settings = testutils::user_settings(); + let (_temp_dir, repo) = testutils::init_repo(&settings, false); + + let mut tx = repo.start_transaction("test"); + let mut_repo = tx.mut_repo(); + let head_unchanged = testutils::create_random_commit(&settings, &repo).write_to_repo(mut_repo); + let head_remove_tx1 = testutils::create_random_commit(&settings, &repo).write_to_repo(mut_repo); + let head_remove_tx2 = testutils::create_random_commit(&settings, &repo).write_to_repo(mut_repo); + let public_head_unchanged = + testutils::create_random_commit(&settings, &repo).write_to_repo(mut_repo); + mut_repo.add_public_head(&public_head_unchanged); + let public_head_remove_tx1 = + testutils::create_random_commit(&settings, &repo).write_to_repo(mut_repo); + mut_repo.add_public_head(&public_head_remove_tx1); + let public_head_remove_tx2 = + testutils::create_random_commit(&settings, &repo).write_to_repo(mut_repo); + mut_repo.add_public_head(&public_head_remove_tx2); + let repo = tx.commit(); + + let mut tx1 = repo.start_transaction("test"); + tx1.mut_repo().remove_head(&head_remove_tx1); + tx1.mut_repo().remove_public_head(&public_head_remove_tx1); + let head_add_tx1 = + testutils::create_random_commit(&settings, &repo).write_to_repo(tx1.mut_repo()); + let public_head_add_tx1 = + testutils::create_random_commit(&settings, &repo).write_to_repo(tx1.mut_repo()); + tx1.mut_repo().add_public_head(&public_head_add_tx1); + tx1.commit(); + + let mut tx2 = repo.start_transaction("test"); + tx2.mut_repo().remove_head(&head_remove_tx2); + tx2.mut_repo().remove_public_head(&public_head_remove_tx2); + let head_add_tx2 = + testutils::create_random_commit(&settings, &repo).write_to_repo(tx2.mut_repo()); + let public_head_add_tx2 = + testutils::create_random_commit(&settings, &repo).write_to_repo(tx2.mut_repo()); + tx2.mut_repo().add_public_head(&public_head_add_tx2); + tx2.commit(); + + let repo = repo.reload(); + + let expected_heads = hashset! { + repo.view().checkout().clone(), + head_unchanged.id().clone(), + head_add_tx1.id().clone(), + head_add_tx2.id().clone(), + public_head_unchanged.id().clone(), + public_head_remove_tx1.id().clone(), + public_head_remove_tx2.id().clone(), + public_head_add_tx1.id().clone(), + public_head_add_tx2.id().clone(), + }; + assert_eq!(repo.view().heads(), &expected_heads); + + let expected_public_heads = hashset! { + public_head_unchanged.id().clone(), + public_head_add_tx1.id().clone(), + public_head_add_tx2.id().clone(), + }; + assert_eq!(repo.view().public_heads(), &expected_public_heads); +} + +#[test] +fn test_merge_views_checkout() { + // Tests merging of the view's checkout (by performing concurrent operations). + let settings = testutils::user_settings(); + let (_temp_dir, repo) = testutils::init_repo(&settings, false); + + let mut tx1 = repo.start_transaction("test"); + let checkout_tx1 = testutils::create_random_commit(&settings, &repo) + .set_open(false) + .write_to_repo(tx1.mut_repo()); + tx1.mut_repo().set_checkout(checkout_tx1.id().clone()); + tx1.commit(); + + let mut tx2 = repo.start_transaction("test"); + let checkout_tx2 = testutils::create_random_commit(&settings, &repo) + .set_open(false) + .write_to_repo(tx2.mut_repo()); + tx2.mut_repo().set_checkout(checkout_tx2.id().clone()); + tx2.commit(); + + let repo = repo.reload(); + + // We currently arbitrarily pick the first transaction's checkout (first by + // transaction end time). + assert_eq!(repo.view().checkout(), checkout_tx1.id()); +} + +#[test] +fn test_merge_views_git_refs() { + // Tests merging of git refs (by performing concurrent operations). See + // test_refs.rs for tests of merging of individual ref targets. + let settings = testutils::user_settings(); + let (_temp_dir, repo) = testutils::init_repo(&settings, false); + + let mut tx = repo.start_transaction("test"); + let mut_repo = tx.mut_repo(); + let main_branch_tx0 = testutils::create_random_commit(&settings, &repo).write_to_repo(mut_repo); + mut_repo.set_git_ref( + "refs/heads/main".to_string(), + RefTarget::Normal(main_branch_tx0.id().clone()), + ); + let feature_branch_tx0 = + testutils::create_random_commit(&settings, &repo).write_to_repo(mut_repo); + mut_repo.set_git_ref( + "refs/heads/feature".to_string(), + RefTarget::Normal(feature_branch_tx0.id().clone()), + ); + let repo = tx.commit(); + + let mut tx1 = repo.start_transaction("test"); + let main_branch_tx1 = + testutils::create_random_commit(&settings, &repo).write_to_repo(tx1.mut_repo()); + tx1.mut_repo().set_git_ref( + "refs/heads/main".to_string(), + RefTarget::Normal(main_branch_tx1.id().clone()), + ); + let feature_branch_tx1 = + testutils::create_random_commit(&settings, &repo).write_to_repo(tx1.mut_repo()); + tx1.mut_repo().set_git_ref( + "refs/heads/feature".to_string(), + RefTarget::Normal(feature_branch_tx1.id().clone()), + ); + tx1.commit(); + + let mut tx2 = repo.start_transaction("test"); + let main_branch_tx2 = + testutils::create_random_commit(&settings, &repo).write_to_repo(tx2.mut_repo()); + tx2.mut_repo().set_git_ref( + "refs/heads/main".to_string(), + RefTarget::Normal(main_branch_tx2.id().clone()), + ); + tx2.commit(); + + let repo = repo.reload(); + let expected_main_branch = RefTarget::Conflict { + removes: vec![main_branch_tx0.id().clone()], + adds: vec![main_branch_tx1.id().clone(), main_branch_tx2.id().clone()], + }; + let expected_feature_branch = RefTarget::Normal(feature_branch_tx1.id().clone()); + assert_eq!( + repo.view().git_refs(), + &btreemap! { + "refs/heads/main".to_string() => expected_main_branch, + "refs/heads/feature".to_string() => expected_feature_branch, + } + ); +}