use std::path::PathBuf;
use std::process::Command;
fn bin_path() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_molpack"))
}
fn example_dir(name: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("examples")
.join(name)
}
#[test]
fn exits_nonzero_on_missing_output_keyword() {
let out = Command::new(bin_path())
.args(["--"])
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.spawn()
.expect("spawn")
.wait_with_output()
.expect("wait");
assert!(!out.status.success());
}
#[test]
fn exits_nonzero_on_nonexistent_input_file() {
let out = Command::new(bin_path())
.arg("does_not_exist.inp")
.output()
.expect("run");
assert!(!out.status.success());
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("cannot read"),
"expected 'cannot read' in stderr, got: {stderr}"
);
}
#[test]
fn exits_nonzero_on_parse_error() {
let bad_inp = "tolerance 2.0\noutput x.pdb\nstructure mol.pdb\n number 1\n";
let out = Command::new(bin_path())
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.spawn()
.and_then(|mut child| {
use std::io::Write;
child
.stdin
.as_mut()
.unwrap()
.write_all(bad_inp.as_bytes())?;
child.wait_with_output()
})
.expect("run");
assert!(!out.status.success());
}
#[test]
fn exits_nonzero_on_unknown_top_level_keyword() {
let bad_inp = "\
tolerance 2.0
output /tmp/x.pdb
wibble 1 2 3
structure mol.pdb
number 1
inside box 0. 0. 0. 20. 20. 20.
end structure
";
let out = Command::new(bin_path())
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::piped())
.spawn()
.and_then(|mut child| {
use std::io::Write;
child
.stdin
.as_mut()
.unwrap()
.write_all(bad_inp.as_bytes())?;
child.wait_with_output()
})
.expect("run");
assert!(
!out.status.success(),
"CLI should exit non-zero on unknown keyword"
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("wibble"),
"expected the offending keyword to appear in stderr, got: {stderr}"
);
}
#[test]
fn smoke_pack_mixture() {
let dir = example_dir("pack_mixture");
let out_path = dir.join("_ci_mixture.pdb");
let _ = std::fs::remove_file(&out_path);
let inp = format!(
"tolerance 2.0\nseed 1234567\nfiletype pdb\noutput {}\n\n\
structure water.pdb\n number 50\n inside box 0. 0. 0. 20. 20. 20.\nend structure\n",
out_path.display()
);
let out = Command::new(bin_path())
.current_dir(&dir)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.spawn()
.and_then(|mut child| {
use std::io::Write;
child.stdin.as_mut().unwrap().write_all(inp.as_bytes())?;
child.wait_with_output()
})
.expect("run molpack");
assert!(
out.status.success(),
"molpack exited {:?}\nstdout: {}\nstderr: {}",
out.status,
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
assert!(
out_path.exists(),
"output file not created: {}",
out_path.display()
);
let _ = std::fs::remove_file(&out_path);
}
#[test]
fn smoke_pack_pbc_only_finishes_quickly() {
let dir = example_dir("pack_mixture");
let out_path = dir.join("_ci_pbc_only.pdb");
let _ = std::fs::remove_file(&out_path);
let inp = format!(
"tolerance 2.0\nseed 1234567\nfiletype pdb\noutput {}\n\
pbc 30.0 30.0 30.0\n\n\
structure water.pdb\n number 10\nend structure\n",
out_path.display()
);
let start = std::time::Instant::now();
let out = Command::new(bin_path())
.current_dir(&dir)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::piped())
.spawn()
.and_then(|mut child| {
use std::io::Write;
child.stdin.as_mut().unwrap().write_all(inp.as_bytes())?;
child.wait_with_output()
})
.expect("run molpack");
let elapsed = start.elapsed();
let _ = std::fs::remove_file(&out_path);
assert!(
out.status.success(),
"molpack exited {:?}\nstderr: {}",
out.status,
String::from_utf8_lossy(&out.stderr)
);
assert!(
elapsed.as_secs() < 30,
"pbc-only pack should finish fast; took {elapsed:?}"
);
}
#[test]
fn file_arg_resolves_paths_from_inp_dir() {
let dir = example_dir("pack_mixture");
let out_path = dir.join("_ci_mixture_filearg.pdb");
let _ = std::fs::remove_file(&out_path);
let inp = format!(
"tolerance 2.0\nseed 42\nfiletype pdb\noutput {}\n\n\
structure water.pdb\n number 30\n inside box 0. 0. 0. 15. 15. 15.\nend structure\n",
out_path.display()
);
let inp_path = dir.join("_ci_test.inp");
std::fs::write(&inp_path, &inp).expect("write inp");
let out = Command::new(bin_path())
.arg(&inp_path)
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.output()
.expect("run molpack");
let _ = std::fs::remove_file(&inp_path);
let _ = std::fs::remove_file(&out_path);
assert!(
out.status.success(),
"molpack exited {:?}\nstdout: {}\nstderr: {}",
out.status,
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
}