mod helpers;
use helpers::*;
const REF20: &str = "CACACACACACACACACACA"; const REF10: &str = "CACACACACA";
fn ot_r1() -> u16 {
FLAG_PAIRED | FLAG_PROPER_PAIR | FLAG_FIRST_SEGMENT | FLAG_MATE_REVERSE
}
fn ot_r2() -> u16 {
FLAG_PAIRED | FLAG_PROPER_PAIR | FLAG_LAST_SEGMENT | FLAG_REVERSE
}
fn ca_total(env: &TestEnv) -> u64 {
genome_stats(&env.stats)["CA_total"].parse().unwrap()
}
fn ca_unconv(env: &TestEnv) -> u64 {
genome_stats(&env.stats)["CA_unconv"].parse().unwrap()
}
#[test]
fn overlap_dedup_keeps_template_below_threshold() {
let reference = RefBuilder::new().contig("chr1", REF10);
let sam = SamBuilder::new()
.sq("chr1", REF10.len())
.record("p", ot_r1(), "chr1", 1, "6M", "CACATA", &q40(6))
.record("p", ot_r2(), "chr1", 1, "6M", "CACATA", &q40(6));
let env = TestEnv::new();
let deduped = run_ok(&sam, &reference, &env, &[]);
assert!(
deduped.iter().all(|r| !has_tag(r, [b'X', b'X'])),
"with overlap dedup, 2 unique unconverted < 3 → not tagged"
);
}
#[test]
fn each_overlapped_reference_base_counted_once() {
let reference = RefBuilder::new().contig("chr1", REF20);
let sam = SamBuilder::new()
.sq("chr1", REF20.len())
.record("p", ot_r1(), "chr1", 1, "10M", "CACACACACA", &q40(10)) .record("p", ot_r2(), "chr1", 6, "10M", "ACACACACAC", &q40(10));
let env = TestEnv::new();
let stats = env.stats.to_str().unwrap().to_string();
run_ok(&sam, &reference, &env, &["--stats", &stats]);
assert_eq!(ca_total(&env), 8, "overlapped C@6,8 counted once");
}
#[test]
fn overlap_split_assigns_each_half_to_the_nearer_mate() {
let reference = RefBuilder::new().contig("chr1", REF10);
let sam = SamBuilder::new()
.sq("chr1", REF10.len())
.record("p", ot_r1(), "chr1", 1, "10M", "CACACACACA", &q40(10))
.record("p", ot_r2(), "chr1", 1, "10M", "TATATATATA", &q40(10));
let env = TestEnv::new();
let stats = env.stats.to_str().unwrap().to_string();
run_ok(&sam, &reference, &env, &["--stats", &stats]);
assert_eq!(ca_total(&env), 5, "5 distinct CpA sites across the split overlap");
assert_eq!(ca_unconv(&env), 3, "only R1's half [0,5) contributes unconverted C's");
}
#[test]
fn non_overlapping_pair_is_unchanged() {
let reference = RefBuilder::new().contig("chr1", REF20);
let sam = SamBuilder::new()
.sq("chr1", REF20.len())
.record("p", ot_r1(), "chr1", 1, "10M", "CACACACACA", &q40(10)) .record("p", ot_r2(), "chr1", 11, "10M", "CACACACACA", &q40(10));
let env = TestEnv::new();
let stats = env.stats.to_str().unwrap().to_string();
run_ok(&sam, &reference, &env, &["--stats", &stats]);
assert_eq!(ca_total(&env), 10, "non-overlapping mates both fully counted");
}
#[test]
fn single_end_reads_are_unaffected_by_dedup() {
let reference = RefBuilder::new().contig("chr1", REF10);
let sam = SamBuilder::new()
.sq("chr1", REF10.len())
.record("a", 0, "chr1", 1, "10M", "CACACACACA", &q40(10))
.record("b", 0, "chr1", 1, "10M", "CACACACACA", &q40(10));
let env = TestEnv::new();
let stats = env.stats.to_str().unwrap().to_string();
run_ok(&sam, &reference, &env, &["--stats", &stats]);
assert_eq!(ca_total(&env), 10);
}