use std::process::Command;
use tempfile::TempDir;
fn samtools_available() -> bool {
Command::new("samtools").arg("--version").output().map(|o| o.status.success()).unwrap_or(false)
}
fn fgumi_simulate_available() -> bool {
let output = Command::new("cargo")
.args(["build", "--features", "simulate", "--message-format=short"])
.output();
output.map(|o| o.status.success()).unwrap_or(false)
}
fn verify_template_coordinate_sorted(bam_path: &std::path::Path) -> bool {
let temp_dir = TempDir::new().unwrap();
let sorted_path = temp_dir.path().join("sorted.bam");
let status = Command::new("samtools")
.args([
"sort",
"--template-coordinate",
"-o",
sorted_path.to_str().unwrap(),
bam_path.to_str().unwrap(),
])
.status()
.expect("Failed to run samtools sort");
if !status.success() {
return false;
}
let original = Command::new("samtools")
.args(["view", bam_path.to_str().unwrap()])
.output()
.expect("Failed to read original BAM");
let sorted = Command::new("samtools")
.args(["view", sorted_path.to_str().unwrap()])
.output()
.expect("Failed to read sorted BAM");
original.stdout == sorted.stdout
}
#[test]
#[ignore = "requires samtools and fgumi with simulate feature"]
fn test_mapped_reads_matches_samtools() {
if !samtools_available() {
eprintln!("Skipping: samtools not available");
return;
}
if !fgumi_simulate_available() {
eprintln!("Skipping: fgumi simulate feature not available");
return;
}
let temp_dir = TempDir::new().unwrap();
let bam_path = temp_dir.path().join("mapped.bam");
let truth_path = temp_dir.path().join("truth.tsv");
let status = Command::new("cargo")
.args([
"run",
"--features",
"simulate",
"--release",
"--",
"simulate",
"mapped-reads",
"-o",
bam_path.to_str().unwrap(),
"--truth",
truth_path.to_str().unwrap(),
"--num-molecules",
"1000",
"--seed",
"42",
])
.status()
.expect("Failed to run fgumi simulate mapped-reads");
assert!(status.success(), "fgumi simulate mapped-reads failed");
assert!(bam_path.exists(), "BAM file not created");
assert!(
verify_template_coordinate_sorted(&bam_path),
"mapped-reads output does not match samtools sort --template-coordinate"
);
}
#[test]
#[ignore = "requires samtools and fgumi with simulate feature"]
fn test_grouped_reads_simplex_matches_samtools() {
if !samtools_available() {
eprintln!("Skipping: samtools not available");
return;
}
if !fgumi_simulate_available() {
eprintln!("Skipping: fgumi simulate feature not available");
return;
}
let temp_dir = TempDir::new().unwrap();
let bam_path = temp_dir.path().join("grouped.bam");
let truth_path = temp_dir.path().join("truth.tsv");
let status = Command::new("cargo")
.args([
"run",
"--features",
"simulate",
"--release",
"--",
"simulate",
"grouped-reads",
"-o",
bam_path.to_str().unwrap(),
"--truth",
truth_path.to_str().unwrap(),
"--num-molecules",
"1000",
"--seed",
"42",
])
.status()
.expect("Failed to run fgumi simulate grouped-reads");
assert!(status.success(), "fgumi simulate grouped-reads failed");
assert!(bam_path.exists(), "BAM file not created");
assert!(
verify_template_coordinate_sorted(&bam_path),
"grouped-reads simplex output does not match samtools sort --template-coordinate"
);
}
#[test]
#[ignore = "requires samtools and fgumi with simulate feature"]
fn test_grouped_reads_duplex_matches_samtools() {
if !samtools_available() {
eprintln!("Skipping: samtools not available");
return;
}
if !fgumi_simulate_available() {
eprintln!("Skipping: fgumi simulate feature not available");
return;
}
let temp_dir = TempDir::new().unwrap();
let bam_path = temp_dir.path().join("grouped_duplex.bam");
let truth_path = temp_dir.path().join("truth.tsv");
let status = Command::new("cargo")
.args([
"run",
"--features",
"simulate",
"--release",
"--",
"simulate",
"grouped-reads",
"-o",
bam_path.to_str().unwrap(),
"--truth",
truth_path.to_str().unwrap(),
"--num-molecules",
"1000",
"--duplex",
"--seed",
"42",
])
.status()
.expect("Failed to run fgumi simulate grouped-reads --duplex");
assert!(status.success(), "fgumi simulate grouped-reads --duplex failed");
assert!(bam_path.exists(), "BAM file not created");
assert!(
verify_template_coordinate_sorted(&bam_path),
"grouped-reads duplex output does not match samtools sort --template-coordinate"
);
}
#[test]
#[ignore = "requires samtools and fgumi with simulate feature; slow"]
fn test_large_dataset_matches_samtools() {
if !samtools_available() {
eprintln!("Skipping: samtools not available");
return;
}
if !fgumi_simulate_available() {
eprintln!("Skipping: fgumi simulate feature not available");
return;
}
let temp_dir = TempDir::new().unwrap();
let bam_path = temp_dir.path().join("large.bam");
let truth_path = temp_dir.path().join("truth.tsv");
let status = Command::new("cargo")
.args([
"run",
"--features",
"simulate",
"--release",
"--",
"simulate",
"grouped-reads",
"-o",
bam_path.to_str().unwrap(),
"--truth",
truth_path.to_str().unwrap(),
"--num-molecules",
"10000",
"--duplex",
"--seed",
"12345",
])
.status()
.expect("Failed to run fgumi simulate grouped-reads");
assert!(status.success());
assert!(verify_template_coordinate_sorted(&bam_path));
}