tests: include git runtime and prepare preview1 fixtures from the bundle
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
//! Git stays responsive through the shell, and cancellation removes only its own index lock.
|
||||
use sausage::{
|
||||
exec::{Command, Exec, Options},
|
||||
process::Runtime,
|
||||
world::World,
|
||||
};
|
||||
use sausage_vfs::{Layer, Open};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, OnceLock},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
fn guests() -> &'static Path {
|
||||
static BUILT: OnceLock<PathBuf> = OnceLock::new();
|
||||
BUILT.get_or_init(|| {
|
||||
let target = crate::common::build_git();
|
||||
assert!(crate::common::build_brush().unwrap().success());
|
||||
target.join("wasm32-wasip2/release")
|
||||
})
|
||||
}
|
||||
fn native(cwd: &Path, args: &[&str]) {
|
||||
let output = std::process::Command::new("git")
|
||||
.args(args)
|
||||
.current_dir(cwd)
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
struct Fixture {
|
||||
_temp: tempfile::TempDir,
|
||||
runtime: Arc<Runtime>,
|
||||
root: Arc<dyn Layer>,
|
||||
processes: sausage::process::Processes,
|
||||
}
|
||||
fn fixture() -> Fixture {
|
||||
let programs = guests();
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let root = temp.path().join("world");
|
||||
for path in ["bin", "src/demo/src", "worktrees"] {
|
||||
std::fs::create_dir_all(root.join(path)).unwrap();
|
||||
}
|
||||
for (source, name) in [("brush", "sh"), ("git", "git")] {
|
||||
std::fs::copy(
|
||||
programs.join(format!("{source}.wasm")),
|
||||
root.join(format!("bin/{name}.wasm")),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
let repo = root.join("src/demo");
|
||||
std::fs::write(repo.join("src/main.rs"), "old\n").unwrap();
|
||||
native(&repo, &["init", "-b", "main"]);
|
||||
native(&repo, &["add", "."]);
|
||||
native(
|
||||
&repo,
|
||||
&[
|
||||
"-c",
|
||||
"user.name=Test",
|
||||
"-c",
|
||||
"user.email=test@example.com",
|
||||
"commit",
|
||||
"--no-gpg-sign",
|
||||
"-m",
|
||||
"initial",
|
||||
],
|
||||
);
|
||||
native(
|
||||
&repo,
|
||||
&[
|
||||
"worktree",
|
||||
"add",
|
||||
"-b",
|
||||
"worker",
|
||||
root.join("worktrees/160").to_str().unwrap(),
|
||||
],
|
||||
);
|
||||
std::fs::write(
|
||||
root.join("worktrees/160/.git"),
|
||||
"gitdir: /src/demo/.git/worktrees/160\n",
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(
|
||||
repo.join(".git/worktrees/160/gitdir"),
|
||||
"/worktrees/160/.git\n",
|
||||
)
|
||||
.unwrap();
|
||||
let world = World::open(temp.path()).unwrap();
|
||||
let root = world.root.clone();
|
||||
let processes = world.processes.clone();
|
||||
let runtime = Runtime::new(world).unwrap();
|
||||
Fixture {
|
||||
_temp: temp,
|
||||
runtime,
|
||||
root,
|
||||
processes,
|
||||
}
|
||||
}
|
||||
fn options() -> Options {
|
||||
Options {
|
||||
cwd: "/worktrees/160".into(),
|
||||
env: [
|
||||
("GIT_AUTHOR_NAME", "Test"),
|
||||
("GIT_AUTHOR_EMAIL", "test@example.com"),
|
||||
("GIT_COMMITTER_NAME", "Test"),
|
||||
("GIT_COMMITTER_EMAIL", "test@example.com"),
|
||||
]
|
||||
.map(|(k, v)| (k.into(), v.into()))
|
||||
.into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
async fn run(fixture: &Fixture, script: &str) -> String {
|
||||
let mut output = Vec::new();
|
||||
let code = fixture
|
||||
.runtime
|
||||
.exec(Command::Shell(script.into()), options(), &mut |_, bytes| {
|
||||
output.extend_from_slice(bytes)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(code, 0, "{}", String::from_utf8_lossy(&output));
|
||||
String::from_utf8(output).unwrap()
|
||||
}
|
||||
async fn edit(fixture: &Fixture) {
|
||||
let file = fixture
|
||||
.root
|
||||
.open(
|
||||
"/worktrees/160/src/main.rs",
|
||||
Open {
|
||||
write: true,
|
||||
truncate: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
file.write(0, b"changed\n".to_vec()).await.unwrap();
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn linked_worktree_commands_stay_under_two_seconds() {
|
||||
let fixture = fixture();
|
||||
run(&fixture, "git status --short").await;
|
||||
edit(&fixture).await;
|
||||
for script in [
|
||||
"git status --short",
|
||||
"git log -1",
|
||||
"git add src/main.rs && git commit --no-gpg-sign -m changed",
|
||||
] {
|
||||
let start = std::time::Instant::now();
|
||||
let output = run(&fixture, script).await;
|
||||
let elapsed = start.elapsed();
|
||||
println!("{script}: {elapsed:?}\n{output}");
|
||||
assert!(elapsed < Duration::from_secs(2), "{script}: {elapsed:?}");
|
||||
}
|
||||
}
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn cancelled_add_releases_its_index_lock() {
|
||||
let fixture = fixture();
|
||||
run(&fixture, "git status --short").await;
|
||||
edit(&fixture).await;
|
||||
let path = "/src/demo/.git/worktrees/160/index.lock";
|
||||
let mut changes = fixture
|
||||
.root
|
||||
.observer()
|
||||
.unwrap()
|
||||
.subscribe(vec![path.into()]);
|
||||
let mut output = Vec::new();
|
||||
let mut collect = |_, bytes: &[u8]| output.extend_from_slice(bytes);
|
||||
let mut command = Box::pin(fixture.runtime.exec(
|
||||
Command::Argv(vec![
|
||||
"/bin/git.wasm".into(),
|
||||
"add".into(),
|
||||
"src/main.rs".into(),
|
||||
]),
|
||||
options(),
|
||||
&mut collect,
|
||||
));
|
||||
tokio::time::timeout(Duration::from_secs(30), async {
|
||||
tokio::select! {
|
||||
biased;
|
||||
change = changes.recv() => { assert_eq!(change.unwrap().0, path); },
|
||||
result = &mut command => panic!("add completed before cancellation: {result:?}"),
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(
|
||||
!fixture.processes.lock().unwrap().is_empty(),
|
||||
"Git must still run when cancellation occurs"
|
||||
);
|
||||
drop(command);
|
||||
tokio::time::timeout(Duration::from_secs(30), async {
|
||||
while !fixture.processes.lock().unwrap().is_empty() {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(fixture.root.stat(path, false).await.is_err());
|
||||
run(
|
||||
&fixture,
|
||||
"git add src/main.rs && git commit --no-gpg-sign -m recovered",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Reference in New Issue
Block a user