Skip to content

Commit

Permalink
working_copy: add test of racy checkout followed by file write
Browse files Browse the repository at this point in the history
We don't seem to have any tests that our protection from undetected
changes caused by writes happening right after checkout, so let's add
one. The test case loops 100 times and each iteration fails slightly
more than 80% of the time on my machine (if I remove the protection in
`TreeState::update_file_state()`), so it seems quite good at
triggering the race.
  • Loading branch information
martinvonz committed Jul 24, 2023
1 parent e364c49 commit 8d1cb1e
Showing 1 changed file with 43 additions and 1 deletion.
44 changes: 43 additions & 1 deletion lib/tests/test_working_copy_concurrent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ use std::cmp::max;
use std::thread;

use assert_matches::assert_matches;
use jj_lib::backend::TreeId;
use jj_lib::op_store::OperationId;
use jj_lib::repo::{Repo, StoreFactories};
use jj_lib::repo_path::RepoPath;
use jj_lib::working_copy::{CheckoutError, SnapshotOptions};
use jj_lib::workspace::Workspace;
use testutils::TestWorkspace;
use testutils::{write_working_copy_file, TestWorkspace};

#[test]
fn test_concurrent_checkout() {
Expand Down Expand Up @@ -143,3 +145,43 @@ fn test_checkout_parallel() {
}
});
}

#[test]
fn test_racy_checkout() {
let settings = testutils::user_settings();
let mut test_workspace = TestWorkspace::init(&settings, true);
let repo = &test_workspace.repo;
let workspace_root = test_workspace.workspace.workspace_root().clone();

let path = RepoPath::from_internal_string("file");
let tree = testutils::create_tree(repo, &[(&path, "1")]);

fn snapshot(workspace: &mut Workspace) -> TreeId {
let mut locked_wc = workspace.working_copy_mut().start_mutation().unwrap();
let tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
locked_wc.finish(OperationId::from_hex("abc123")).unwrap();
tree_id
}

let mut num_matches = 0;
for _ in 0..100 {
let wc = test_workspace.workspace.working_copy_mut();
wc.check_out(repo.op_id().clone(), None, &tree).unwrap();
assert_eq!(
std::fs::read(path.to_fs_path(&workspace_root)).unwrap(),
b"1".to_vec()
);
// A file written right after checkout (hopefully, from the test's perspective,
// within the file system timestamp granularity) is detected as changed.
write_working_copy_file(&workspace_root, &path, "x");
let modified_tree_id = snapshot(&mut test_workspace.workspace);
if modified_tree_id == *tree.id() {
num_matches += 1;
}
// Reset the state for the next round
write_working_copy_file(&workspace_root, &path, "1");
}
assert_eq!(num_matches, 0);
}

0 comments on commit 8d1cb1e

Please sign in to comment.