use openmassspec_core::conformance::assert_source_invariants;
use openmassspec_core::SpectrumSource;
use openszraw::reader::Reader;
use std::path::{Path, PathBuf};
fn pick_fixture(candidates: &[PathBuf]) -> PathBuf {
candidates
.iter()
.find(|p| p.exists())
.cloned()
.unwrap_or_else(|| candidates[0].clone())
}
fn qgd_profile_fixture() -> PathBuf {
pick_fixture(&[
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../corpus/PXD019638_L-B2-Br0-1.qgd"),
PathBuf::from("/workspaces/Projects/Data/SZRaw/PXD019638/L-B2-Br0-1.qgd"),
])
}
fn qgd_mrm_fixture() -> PathBuf {
pick_fixture(&[
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../corpus/PXD034978_49_27a__8122021_11.qgd"),
PathBuf::from("/workspaces/Projects/Data/SZRaw/PXD034978/49_27a__8122021_11.qgd"),
])
}
fn qtfl_fixture() -> PathBuf {
PathBuf::from("/workspaces/Projects/Data/SZRaw/MSV000084197/20190607_NM16.lcd")
}
fn ttfl_fixture() -> PathBuf {
PathBuf::from("/workspaces/Projects/Data/SZRaw/PXD020792/UY02-01-01p400.LCD")
}
fn ttfl_lc_chromatogram_fixture() -> PathBuf {
ttfl_fixture()
}
fn ttfl_partial_block_fixture() -> PathBuf {
PathBuf::from("/workspaces/Projects/Data/SZRaw/PXD025121/17.lcd")
}
fn single_quad_fixture() -> PathBuf {
PathBuf::from("/workspaces/Projects/Data/SZRaw/MTBLS1960/E1.lcd")
}
fn open_or_skip(path: &Path) -> Option<Reader> {
if !path.exists() {
eprintln!("skip: corpus not present at {}", path.display());
return None;
}
Some(Reader::open(path).unwrap_or_else(|e| panic!("Reader::open({path:?}) failed: {e}")))
}
#[test]
fn qgd_profile_conformance() {
let Some(mut reader) = open_or_skip(&qgd_profile_fixture()) else {
return;
};
let n = assert_source_invariants(&mut reader).expect("conformance invariants failed");
assert!(n > 0, "expected at least one spectrum");
println!("qgd profile: {n} spectra");
}
#[test]
fn qgd_profile_peaks_are_plausible() {
let Some(mut reader) = open_or_skip(&qgd_profile_fixture()) else {
return;
};
let spectra: Vec<_> = reader.iter_spectra().collect();
assert!(!spectra.is_empty());
assert!(spectra.iter().all(|s| s.ms_level == 1));
let with_peaks: Vec<_> = spectra.iter().filter(|s| !s.mz.is_empty()).collect();
assert!(
!with_peaks.is_empty(),
"expected at least one spectrum with peaks"
);
for s in &with_peaks {
for &mz in &s.mz {
assert!(
(1.0..=2000.0).contains(&mz),
"implausible GC-MS m/z {mz} in spectrum {}",
s.native_id
);
}
}
let mut last_rt = f64::MIN;
for s in &spectra {
assert!(
s.retention_time_sec + 1e-6 >= last_rt,
"RT regressed: {} -> {}",
last_rt,
s.retention_time_sec
);
last_rt = s.retention_time_sec;
}
}
#[test]
fn qgd_mrm_conformance() {
let Some(mut reader) = open_or_skip(&qgd_mrm_fixture()) else {
return;
};
let n = assert_source_invariants(&mut reader).expect("conformance invariants failed");
assert!(n > 0, "expected at least one spectrum");
println!("qgd mrm: {n} spectra");
}
#[test]
fn qgd_mrm_transitions_are_plausible() {
let Some(mut reader) = open_or_skip(&qgd_mrm_fixture()) else {
return;
};
let spectra: Vec<_> = reader.iter_spectra().collect();
assert!(!spectra.is_empty());
assert!(spectra.iter().all(|s| s.ms_level == 2));
assert!(spectra
.iter()
.all(|s| s.mz.len() == 1 && s.intensity.len() == 1));
for s in &spectra {
let precursor = s
.precursor
.as_ref()
.unwrap_or_else(|| panic!("MRM spectrum {} missing precursor", s.native_id));
let precursor_mz = precursor.selected_mz.expect("selected_mz set for MRM");
assert!(
(1.0..=2000.0).contains(&precursor_mz),
"implausible precursor m/z {precursor_mz}"
);
let product_mz = s.mz[0];
assert!(
(1.0..=2000.0).contains(&product_mz),
"implausible product m/z {product_mz}"
);
}
}
#[test]
fn qtfl_conformance() {
let Some(mut reader) = open_or_skip(&qtfl_fixture()) else {
return;
};
let n = assert_source_invariants(&mut reader).expect("conformance invariants failed");
assert!(n > 0, "expected at least one spectrum");
println!("qtfl: {n} spectra");
}
#[test]
fn qtfl_centroid_mz_is_plausible_and_has_ms2() {
let Some(mut reader) = open_or_skip(&qtfl_fixture()) else {
return;
};
let spectra: Vec<_> = reader.iter_spectra().collect();
assert!(!spectra.is_empty());
let has_ms1 = spectra.iter().any(|s| s.ms_level == 1);
let has_ms2 = spectra.iter().any(|s| s.ms_level == 2);
assert!(has_ms1, "expected at least one MS1 (event_id==1) scan");
assert!(
has_ms2,
"expected at least one MS2 (event_id>1) scan - see docs/format/05 addendum"
);
let with_peaks: Vec<_> = spectra.iter().filter(|s| !s.mz.is_empty()).collect();
assert!(!with_peaks.is_empty());
for s in &with_peaks {
for &mz in &s.mz {
assert!(
(50.0..=5000.0).contains(&mz),
"implausible QTOF m/z {mz} in spectrum {}",
s.native_id
);
}
}
for s in spectra.iter().filter(|s| s.ms_level == 2) {
assert!(
s.precursor
.as_ref()
.is_some_and(|p| p.precursor_native_id.is_some()),
"MS2 spectrum {} missing precursor_native_id",
s.native_id
);
}
}
#[test]
fn ttfl_conformance() {
let Some(mut reader) = open_or_skip(&ttfl_fixture()) else {
return;
};
let n = assert_source_invariants(&mut reader).expect("conformance invariants failed");
assert!(n > 0, "expected at least one spectrum");
println!("ttfl: {n} spectra");
}
#[test]
fn ttfl_partial_block_conformance() {
let Some(mut reader) = open_or_skip(&ttfl_partial_block_fixture()) else {
return;
};
let n = assert_source_invariants(&mut reader).expect("conformance invariants failed");
assert!(n > 0, "expected at least one spectrum");
println!("ttfl (2-channel, partial trailing block): {n} spectra");
}
#[test]
fn ttfl_mz_is_calibrated_finite_and_nonnegative() {
let Some(mut reader) = open_or_skip(&ttfl_fixture()) else {
return;
};
let spectra: Vec<_> = reader.iter_spectra().collect();
assert!(!spectra.is_empty());
assert!(spectra.iter().all(|s| s.ms_level == 1));
let with_peaks: Vec<_> = spectra.iter().filter(|s| !s.mz.is_empty()).collect();
assert!(!with_peaks.is_empty());
for s in &with_peaks {
for &mz in &s.mz {
assert!(mz.is_finite(), "non-finite calibrated m/z {mz}");
assert!(mz >= 0.0, "negative calibrated m/z {mz}");
}
}
}
#[test]
fn ttfl_base_peak_mz_is_plausible_for_most_scans() {
let Some(mut reader) = open_or_skip(&ttfl_fixture()) else {
return;
};
let spectra: Vec<_> = reader.iter_spectra().collect();
let base_peaks: Vec<f64> = spectra
.iter()
.filter_map(|s| {
s.intensity
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.total_cmp(b))
.map(|(i, _)| s.mz[i])
})
.collect();
assert!(!base_peaks.is_empty());
let plausible = base_peaks.iter().filter(|&&mz| mz < 20_000.0).count();
let fraction = plausible as f64 / base_peaks.len() as f64;
assert!(
fraction > 0.8,
"only {:.1}% of base peaks fell under 20,000 Da ({} of {})",
fraction * 100.0,
plausible,
base_peaks.len()
);
}
#[test]
fn ttfl_lc_chromatogram_ch6_decodes_and_is_plausible() {
let Some(mut reader) = open_or_skip(&ttfl_lc_chromatogram_fixture()) else {
return;
};
let chroms: Vec<_> = reader.iter_chromatograms().collect();
assert_eq!(
chroms.len(),
1,
"expected exactly one decoded LC chromatogram (Ch6); Ch5 must be skipped"
);
let ch6 = &chroms[0];
assert!(ch6.id.ends_with("Ch6"), "unexpected id: {}", ch6.id);
assert_eq!(ch6.intensity.len(), 7200);
assert_eq!(ch6.time_sec.len(), 7200);
for w in ch6.time_sec.windows(2) {
assert!(
(w[1] - w[0] - 0.5).abs() < 1e-3,
"sample interval not ~0.5s: {} -> {}",
w[0],
w[1]
);
}
for &v in &ch6.intensity {
assert!(v.is_finite(), "non-finite intensity {v}");
assert!(
(0.0..20_000.0).contains(&v),
"implausible LC Ch6 intensity {v}"
);
}
}
#[test]
fn ttfl_lc_chromatogram_ch5_is_not_emitted() {
let Some(mut reader) = open_or_skip(&ttfl_lc_chromatogram_fixture()) else {
return;
};
let chroms: Vec<_> = reader.iter_chromatograms().collect();
assert!(
chroms.iter().all(|c| !c.id.ends_with("Ch5")),
"Ch5 should not be emitted (single repeated value, grammar untestable)"
);
}
#[test]
fn single_quad_conformance() {
let Some(mut reader) = open_or_skip(&single_quad_fixture()) else {
return;
};
let n = assert_source_invariants(&mut reader).expect("conformance invariants failed");
assert!(n > 0, "expected at least one spectrum");
println!("single-quad: {n} spectra");
}
#[test]
fn single_quad_peaks_are_plausible() {
let Some(mut reader) = open_or_skip(&single_quad_fixture()) else {
return;
};
let spectra: Vec<_> = reader.iter_spectra().collect();
assert!(!spectra.is_empty());
assert!(spectra.iter().all(|s| s.ms_level == 1));
let with_peaks: Vec<_> = spectra.iter().filter(|s| !s.mz.is_empty()).collect();
assert!(
!with_peaks.is_empty(),
"expected at least one spectrum with peaks"
);
for s in &with_peaks {
for &mz in &s.mz {
assert!(
(1.0..=3000.0).contains(&mz),
"implausible single-quad m/z {mz} in spectrum {}",
s.native_id
);
}
}
let mut last_rt = f64::MIN;
for s in &spectra {
assert!(
s.retention_time_sec + 1e-6 >= last_rt,
"RT regressed: {} -> {}",
last_rt,
s.retention_time_sec
);
last_rt = s.retention_time_sec;
}
}