Skip to content

Commit ec3b43a

Browse files
authored
Merge pull request #97 from ArcInstitute/test/coverage
Test/coverage
2 parents fc5889d + ee703e9 commit ec3b43a

23 files changed

Lines changed: 1827 additions & 150 deletions

.github/workflows/ci.yml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
name: CI
22

3-
on: [push, pull_request]
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
47

58
env:
69
CARGO_TERM_COLOR: always
@@ -9,7 +12,7 @@ jobs:
912
build:
1013
runs-on: ubuntu-latest
1114
steps:
12-
- uses: actions/checkout@v3
15+
- uses: actions/checkout@v7
1316
- name: Build
1417
run: cargo build --verbose
1518
- name: Run tests
@@ -20,19 +23,19 @@ jobs:
2023
fmt_lint:
2124
runs-on: ubuntu-latest
2225
steps:
23-
- uses: actions/checkout@v3
26+
- uses: actions/checkout@v7
2427
- name: Formatting
2528
run: cargo fmt --check
2629
- name: Linting
27-
run: cargo clippy --verbose
30+
run: cargo clippy --all-features --all-targets -- -D warnings
2831

2932
example_grep:
3033
runs-on: ubuntu-latest
3134
strategy:
3235
matrix:
3336
ext: [bq, vbq, cbq]
3437
steps:
35-
- uses: actions/checkout@v3
38+
- uses: actions/checkout@v7
3639
- name: run example ${{ matrix.ext }}
3740
run: cargo run --release --example grep -- ./data/subset.${{ matrix.ext }} "ACGTACGT"
3841

@@ -42,7 +45,7 @@ jobs:
4245
matrix:
4346
ext: [bq, vbq, cbq]
4447
steps:
45-
- uses: actions/checkout@v3
48+
- uses: actions/checkout@v7
4649
- name: run example ${{ matrix.ext }}
4750
run: cargo run --release --example parallel_range -- ./data/subset.${{ matrix.ext }} 4 30 200
4851

@@ -52,7 +55,7 @@ jobs:
5255
matrix:
5356
ext: [bq, vbq, cbq]
5457
steps:
55-
- uses: actions/checkout@v3
58+
- uses: actions/checkout@v7
5659
- name: run example (single) ${{ matrix.ext }}
5760
run: cargo run --release --example write -- ./data/subset_R1.fastq.gz -o ./output.${{ matrix.ext }}
5861
- name: run example (paired) ${{ matrix.ext }}
@@ -64,6 +67,6 @@ jobs:
6467
matrix:
6568
ext: [bq, vbq, cbq]
6669
steps:
67-
- uses: actions/checkout@v3
70+
- uses: actions/checkout@v7
6871
- name: run example ${{ matrix.ext }}
6972
run: cargo run --release --example read -- ./data/subset.${{ matrix.ext }}

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,7 @@ paraseq = ["dep:paraseq", "dep:parking_lot"]
4141
[lints.clippy]
4242
pedantic = { level = "warn", priority = -1 }
4343
cast_possible_truncation = "allow"
44+
cast_precision_loss = "allow"
4445
missing_errors_doc = "allow"
46+
struct_excessive_bools = "allow"
47+
fn_params_excessive_bools = "allow"

examples/auto-write.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,15 @@ impl Args {
6464
fn format(&self) -> Format {
6565
if let Some(format) = self.format {
6666
format
67-
} else {
68-
if let Some(output) = &self.output {
69-
match output.split(".").last() {
70-
Some("bq") => Format::Bq,
71-
Some("vbq") => Format::Vbq,
72-
Some("cbq") => Format::Cbq,
73-
_ => Format::default(),
74-
}
75-
} else {
76-
Format::default()
67+
} else if let Some(output) = &self.output {
68+
match output.split('.').next_back() {
69+
Some("bq") => Format::Bq,
70+
Some("vbq") => Format::Vbq,
71+
Some("cbq") => Format::Cbq,
72+
_ => Format::default(),
7773
}
74+
} else {
75+
Format::default()
7876
}
7977
}
8078
fn bitsize(&self) -> BitSize {
@@ -87,7 +85,7 @@ impl Args {
8785
/// Creates an output file handle
8886
fn ohandle(&self) -> Result<BoxedWriter> {
8987
let path = if let Some(output) = &self.output {
90-
output.to_string()
88+
output.clone()
9189
} else {
9290
format!("{}{}", &self.prefix, self.format().extension())
9391
};

examples/grep.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ impl GrepCounter {
3737
}
3838
impl ParallelProcessor for GrepCounter {
3939
fn process_record<R: binseq::BinseqRecord>(&mut self, record: R) -> binseq::Result<()> {
40-
if self.match_sequence(&record.sseq()) || self.match_sequence(&record.xseq()) {
40+
if self.match_sequence(record.sseq()) || self.match_sequence(record.xseq()) {
4141
self.local_count += 1;
4242
}
4343

examples/parallel_range.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,18 @@ impl ParallelProcessor for RangeProcessor {
3030
let count = self.counter.fetch_add(1, Ordering::Relaxed);
3131

3232
// Print progress every 10,000 records
33-
if count % 10_000 == 0 {
34-
if let Some(tid) = self.tid {
35-
println!(
36-
"Thread {}: Processed {} records (Range: {}-{}, Index: {}, Len: {})",
37-
tid,
38-
count + 1,
39-
self.range_start,
40-
self.range_end,
41-
record.index(),
42-
record.sseq().len(),
43-
);
44-
}
33+
if count.is_multiple_of(10_000)
34+
&& let Some(tid) = self.tid
35+
{
36+
println!(
37+
"Thread {}: Processed {} records (Range: {}-{}, Index: {}, Len: {})",
38+
tid,
39+
count + 1,
40+
self.range_start,
41+
self.range_end,
42+
record.index(),
43+
record.sseq().len(),
44+
);
4545
}
4646

4747
Ok(())

examples/write.rs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,15 @@ impl Args {
7575
fn format(&self) -> Format {
7676
if let Some(format) = self.format {
7777
format
78-
} else {
79-
if let Some(output) = &self.output {
80-
match output.split(".").last() {
81-
Some("bq") => Format::Bq,
82-
Some("vbq") => Format::Vbq,
83-
Some("cbq") => Format::Cbq,
84-
_ => Format::default(),
85-
}
86-
} else {
87-
Format::default()
78+
} else if let Some(output) = &self.output {
79+
match output.split('.').next_back() {
80+
Some("bq") => Format::Bq,
81+
Some("vbq") => Format::Vbq,
82+
Some("cbq") => Format::Cbq,
83+
_ => Format::default(),
8884
}
85+
} else {
86+
Format::default()
8987
}
9088
}
9189
fn bitsize(&self) -> BitSize {
@@ -98,7 +96,7 @@ impl Args {
9896
/// Creates an output file handle
9997
fn ohandle(&self) -> Result<BoxedWriter> {
10098
let path = if let Some(output) = &self.output {
101-
output.to_string()
99+
output.clone()
102100
} else {
103101
format!("{}{}", &self.prefix, self.format().extension())
104102
};
@@ -225,7 +223,7 @@ impl<Rf: paraseq::Record> PairedParallelProcessor<Rf> for Encoder {
225223

226224
fn encode_paired(args: &Args) -> Result<()> {
227225
let mut r1 = fastx::Reader::from_path(&args.input)?;
228-
let mut r2 = fastx::Reader::from_path(&args.input2.as_ref().expect("Missing input2"))?;
226+
let mut r2 = fastx::Reader::from_path(args.input2.as_ref().expect("Missing input2"))?;
229227
let ohandle = args.ohandle()?;
230228

231229
// prepare writer

src/bq/header.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,3 +338,139 @@ impl FileHeader {
338338
Self::from_bytes(&buffer)
339339
}
340340
}
341+
342+
#[cfg(test)]
343+
mod tests {
344+
use super::*;
345+
346+
// ==================== FileHeaderBuilder Tests ====================
347+
348+
#[test]
349+
fn test_builder_default() {
350+
let builder = FileHeaderBuilder::default();
351+
// Missing slen should fail to build
352+
assert!(builder.build().is_err());
353+
}
354+
355+
#[test]
356+
fn test_builder_bitsize() {
357+
let header = FileHeaderBuilder::new()
358+
.slen(64)
359+
.bitsize(BitSize::Four)
360+
.build()
361+
.unwrap();
362+
assert_eq!(header.bits, BitSize::Four);
363+
}
364+
365+
#[test]
366+
fn test_builder_missing_slen() {
367+
let result = FileHeaderBuilder::new().xlen(10).build();
368+
assert!(result.is_err());
369+
}
370+
371+
// ==================== FileHeader Constructor Tests ====================
372+
373+
#[test]
374+
fn test_header_new() {
375+
let header = FileHeader::new(BitSize::Two, 100, true);
376+
assert_eq!(header.slen, 100);
377+
assert_eq!(header.xlen, 0);
378+
assert!(header.flags);
379+
assert!(!header.is_paired());
380+
}
381+
382+
#[test]
383+
fn test_header_new_extended() {
384+
let header = FileHeader::new_extended(BitSize::Four, 100, 50, false);
385+
assert_eq!(header.slen, 100);
386+
assert_eq!(header.xlen, 50);
387+
assert_eq!(header.bits, BitSize::Four);
388+
assert!(header.is_paired());
389+
}
390+
391+
#[test]
392+
fn test_set_bitsize() {
393+
let mut header = FileHeader::new(BitSize::Two, 100, false);
394+
header.set_bitsize(BitSize::Four);
395+
assert_eq!(header.bits, BitSize::Four);
396+
}
397+
398+
// ==================== from_bytes Tests ====================
399+
400+
#[test]
401+
fn test_from_bytes_invalid_format_version() {
402+
let header = FileHeader::new(BitSize::Two, 32, false);
403+
let mut buffer = [0u8; SIZE_HEADER];
404+
let mut cursor = std::io::Cursor::new(&mut buffer[..]);
405+
header.write_bytes(&mut cursor).unwrap();
406+
buffer[4] = 99; // corrupt format version
407+
let result = FileHeader::from_bytes(&buffer);
408+
assert!(result.is_err());
409+
}
410+
411+
#[test]
412+
fn test_from_bytes_four_bit_size() {
413+
let header = FileHeader::new(BitSize::Four, 32, false);
414+
let mut buffer = [0u8; SIZE_HEADER];
415+
let mut cursor = std::io::Cursor::new(&mut buffer[..]);
416+
header.write_bytes(&mut cursor).unwrap();
417+
let parsed = FileHeader::from_bytes(&buffer).unwrap();
418+
assert_eq!(parsed.bits, BitSize::Four);
419+
}
420+
421+
#[test]
422+
fn test_from_bytes_invalid_bitsize() {
423+
let header = FileHeader::new(BitSize::Two, 32, false);
424+
let mut buffer = [0u8; SIZE_HEADER];
425+
let mut cursor = std::io::Cursor::new(&mut buffer[..]);
426+
header.write_bytes(&mut cursor).unwrap();
427+
buffer[13] = 99; // corrupt bitsize byte
428+
let result = FileHeader::from_bytes(&buffer);
429+
assert!(result.is_err());
430+
}
431+
432+
#[test]
433+
fn test_from_bytes_invalid_magic() {
434+
let buffer = [0u8; SIZE_HEADER];
435+
let result = FileHeader::from_bytes(&buffer);
436+
assert!(result.is_err());
437+
}
438+
439+
// ==================== from_buffer Tests ====================
440+
441+
#[test]
442+
fn test_from_buffer_too_small() {
443+
let buffer = [0u8; 10];
444+
let result = FileHeader::from_buffer(&buffer);
445+
assert!(result.is_err());
446+
}
447+
448+
#[test]
449+
fn test_from_buffer_valid() {
450+
let header = FileHeader::new(BitSize::Two, 32, false);
451+
let mut buffer = Vec::new();
452+
header.write_bytes(&mut buffer).unwrap();
453+
buffer.extend_from_slice(&[0u8; 16]); // trailing data beyond header
454+
let parsed = FileHeader::from_buffer(&buffer).unwrap();
455+
assert_eq!(parsed.slen, 32);
456+
}
457+
458+
// ==================== from_reader Tests ====================
459+
460+
#[test]
461+
fn test_from_reader_valid() {
462+
let header = FileHeader::new_extended(BitSize::Two, 32, 16, true);
463+
let mut buffer = Vec::new();
464+
header.write_bytes(&mut buffer).unwrap();
465+
let mut cursor = std::io::Cursor::new(buffer);
466+
let parsed = FileHeader::from_reader(&mut cursor).unwrap();
467+
assert_eq!(parsed, header);
468+
}
469+
470+
#[test]
471+
fn test_from_reader_truncated() {
472+
let mut cursor = std::io::Cursor::new(vec![0u8; 5]);
473+
let result = FileHeader::from_reader(&mut cursor);
474+
assert!(result.is_err());
475+
}
476+
}

0 commit comments

Comments
 (0)