use slow5lib::aux::AuxMeta;
use slow5lib::header::{Header, ReadGroup, RecordCompression, SignalCompression};
use slow5lib::{Slow5Reader, Slow5Writer};
const PARITY_INPUT: &str = "tests/data/parity_input.slow5";
const REF_NONE_NONE: &str = "tests/data/parity/ref_none_none.blow5";
const REF_NONE_SVBZD: &str = "tests/data/parity/ref_none_svbzd.blow5";
const REF_ZSTD_SVBZD: &str = "tests/data/parity/ref_zstd_svbzd.blow5";
const REF_ZLIB_NONE: &str = "tests/data/parity/ref_zlib_none.blow5";
const REF_NONE_EXZD: &str = "tests/data/parity/ref_none_exzd.blow5";
const REF_ZSTD_EXZD: &str = "tests/data/parity/ref_zstd_exzd.blow5";
fn parity_header(rec: RecordCompression, sig: SignalCompression) -> Header {
Header {
version: (0, 2, 0),
num_read_groups: 1,
record_compression: rec,
signal_compression: sig,
read_groups: vec![ReadGroup::default()],
aux_meta: AuxMeta::default(),
}
}
fn read_all_records(path: &str) -> Vec<slow5lib::record::Record> {
let mut reader = Slow5Reader::open(path).unwrap_or_else(|e| panic!("open {path}: {e}"));
reader
.records()
.map(|r| r.unwrap_or_else(|e| panic!("record from {path}: {e}")))
.collect()
}
fn write_records(
path: &str,
records: &[slow5lib::record::Record],
rec: RecordCompression,
sig: SignalCompression,
) {
let header = parity_header(rec, sig);
let mut writer =
Slow5Writer::create(path, header).unwrap_or_else(|e| panic!("create {path}: {e}"));
for r in records {
writer
.write(r)
.unwrap_or_else(|e| panic!("write to {path}: {e}"));
}
writer
.finish()
.unwrap_or_else(|e| panic!("finish {path}: {e}"));
}
#[test]
fn parity_none_none() {
let tmp = "/tmp/slow5lib_parity_none_none.blow5";
let records = read_all_records(PARITY_INPUT);
write_records(
tmp,
&records,
RecordCompression::None,
SignalCompression::None,
);
let got = std::fs::read(tmp).expect("read rust output");
let want = std::fs::read(REF_NONE_NONE).expect("read reference");
assert_eq!(
got,
want,
"none+none output differs from slow5tools reference ({} bytes vs {} bytes)",
got.len(),
want.len()
);
}
#[test]
fn parity_none_svbzd() {
let tmp = "/tmp/slow5lib_parity_none_svbzd.blow5";
let records = read_all_records(PARITY_INPUT);
write_records(
tmp,
&records,
RecordCompression::None,
SignalCompression::SvbZd,
);
let got = std::fs::read(tmp).expect("read rust output");
let want = std::fs::read(REF_NONE_SVBZD).expect("read reference");
assert_eq!(
got,
want,
"none+svbzd output differs from slow5tools reference ({} bytes vs {} bytes)",
got.len(),
want.len()
);
}
#[test]
fn parity_none_exzd() {
let tmp = "/tmp/slow5lib_parity_none_exzd.blow5";
let records = read_all_records(PARITY_INPUT);
write_records(
tmp,
&records,
RecordCompression::None,
SignalCompression::ExZd,
);
let got = std::fs::read(tmp).expect("read rust output");
let want = std::fs::read(REF_NONE_EXZD).expect("read reference");
assert_eq!(
got,
want,
"none+exzd output differs from slow5tools reference ({} bytes vs {} bytes)",
got.len(),
want.len()
);
}
fn check_parity_values(rust_path: &str, ref_path: &str) {
let source = read_all_records(PARITY_INPUT);
let rust_out = read_all_records(rust_path);
let ref_out = read_all_records(ref_path);
assert_eq!(
rust_out.len(),
source.len(),
"rust output record count mismatch"
);
assert_eq!(
ref_out.len(),
source.len(),
"reference record count mismatch"
);
for (i, ((src, got), want)) in source
.iter()
.zip(rust_out.iter())
.zip(ref_out.iter())
.enumerate()
{
assert_eq!(got.read_id, src.read_id, "record {i}: read_id mismatch");
assert_eq!(
got.raw_signal, src.raw_signal,
"record {i}: signal mismatch (rust output)"
);
assert_eq!(
want.read_id, src.read_id,
"record {i}: read_id mismatch (ref)"
);
assert_eq!(
want.raw_signal, src.raw_signal,
"record {i}: signal mismatch (slow5tools reference)"
);
}
}
#[test]
fn parity_zstd_svbzd() {
let tmp = "/tmp/slow5lib_parity_zstd_svbzd.blow5";
let records = read_all_records(PARITY_INPUT);
write_records(
tmp,
&records,
RecordCompression::Zstd,
SignalCompression::SvbZd,
);
check_parity_values(tmp, REF_ZSTD_SVBZD);
}
#[test]
fn parity_zstd_exzd() {
let tmp = "/tmp/slow5lib_parity_zstd_exzd.blow5";
let records = read_all_records(PARITY_INPUT);
write_records(
tmp,
&records,
RecordCompression::Zstd,
SignalCompression::ExZd,
);
check_parity_values(tmp, REF_ZSTD_EXZD);
}
#[test]
fn parity_zlib_none() {
let tmp = "/tmp/slow5lib_parity_zlib_none.blow5";
let records = read_all_records(PARITY_INPUT);
write_records(
tmp,
&records,
RecordCompression::Zlib,
SignalCompression::None,
);
check_parity_values(tmp, REF_ZLIB_NONE);
}
#[test]
fn parity_ref_none_none_readable() {
let source = read_all_records(PARITY_INPUT);
let from_ref = read_all_records(REF_NONE_NONE);
assert_eq!(from_ref.len(), source.len());
for (i, (got, want)) in from_ref.iter().zip(source.iter()).enumerate() {
assert_eq!(got.read_id, want.read_id, "record {i}");
assert_eq!(got.raw_signal, want.raw_signal, "record {i}");
}
}
#[test]
fn parity_ref_none_svbzd_readable() {
let source = read_all_records(PARITY_INPUT);
let from_ref = read_all_records(REF_NONE_SVBZD);
assert_eq!(from_ref.len(), source.len());
for (i, (got, want)) in from_ref.iter().zip(source.iter()).enumerate() {
assert_eq!(got.read_id, want.read_id, "record {i}");
assert_eq!(got.raw_signal, want.raw_signal, "record {i}");
}
}
#[test]
fn parity_ref_zstd_svbzd_readable() {
let source = read_all_records(PARITY_INPUT);
let from_ref = read_all_records(REF_ZSTD_SVBZD);
assert_eq!(from_ref.len(), source.len());
for (i, (got, want)) in from_ref.iter().zip(source.iter()).enumerate() {
assert_eq!(got.read_id, want.read_id, "record {i}");
assert_eq!(got.raw_signal, want.raw_signal, "record {i}");
}
}
#[test]
fn parity_ref_zlib_none_readable() {
let source = read_all_records(PARITY_INPUT);
let from_ref = read_all_records(REF_ZLIB_NONE);
assert_eq!(from_ref.len(), source.len());
for (i, (got, want)) in from_ref.iter().zip(source.iter()).enumerate() {
assert_eq!(got.read_id, want.read_id, "record {i}");
assert_eq!(got.raw_signal, want.raw_signal, "record {i}");
}
}
#[test]
fn parity_ref_none_exzd_readable() {
let source = read_all_records(PARITY_INPUT);
let from_ref = read_all_records(REF_NONE_EXZD);
assert_eq!(from_ref.len(), source.len());
for (i, (got, want)) in from_ref.iter().zip(source.iter()).enumerate() {
assert_eq!(got.read_id, want.read_id, "record {i}");
assert_eq!(got.raw_signal, want.raw_signal, "record {i}");
}
}
#[test]
fn parity_ref_zstd_exzd_readable() {
let source = read_all_records(PARITY_INPUT);
let from_ref = read_all_records(REF_ZSTD_EXZD);
assert_eq!(from_ref.len(), source.len());
for (i, (got, want)) in from_ref.iter().zip(source.iter()).enumerate() {
assert_eq!(got.read_id, want.read_id, "record {i}");
assert_eq!(got.raw_signal, want.raw_signal, "record {i}");
}
}