Skip to content

Commit 3c55918

Browse files
committed
tests: added validity checking on vbq index being written
1 parent 304f6d5 commit 3c55918

1 file changed

Lines changed: 92 additions & 0 deletions

File tree

src/vbq/writer.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ impl<W: Write> Writer<W> {
630630
/// }
631631
/// ```
632632
pub fn finish(&mut self) -> Result<()> {
633+
// Flush any remaining data in the current block
633634
impl_flush_block(
634635
&mut self.inner,
635636
&mut self.cblock,
@@ -639,6 +640,8 @@ impl<W: Write> Writer<W> {
639640
)?;
640641
self.inner.flush()?;
641642

643+
// Always write the index - this is critical for VBQ file validity
644+
// The index_written flag prevents double-writing on subsequent finish() calls
642645
if !self.index_written {
643646
self.write_index()?;
644647
self.index_written = true;
@@ -1511,4 +1514,93 @@ mod tests {
15111514

15121515
Ok(())
15131516
}
1517+
1518+
#[test]
1519+
fn test_index_always_written_on_finish() -> super::Result<()> {
1520+
use crate::vbq::index::INDEX_END_MAGIC;
1521+
use byteorder::{ByteOrder, LittleEndian};
1522+
1523+
// Create a writer with some records
1524+
let header = FileHeaderBuilder::new().build();
1525+
let mut writer = WriterBuilder::default().header(header).build(Vec::new())?;
1526+
1527+
// Write some records
1528+
for i in 0..10 {
1529+
let record = SequencingRecordBuilder::default()
1530+
.s_seq(b"ACGTACGTACGT")
1531+
.flag(i)
1532+
.build()?;
1533+
writer.push(record)?;
1534+
}
1535+
1536+
// Finish the writer
1537+
writer.finish()?;
1538+
1539+
// Get the written bytes
1540+
let bytes = &writer.inner;
1541+
1542+
// Verify the file ends with the index magic number
1543+
assert!(bytes.len() >= 8, "File is too short to contain index");
1544+
let magic_offset = bytes.len() - 8;
1545+
let magic = LittleEndian::read_u64(&bytes[magic_offset..]);
1546+
assert_eq!(
1547+
magic, INDEX_END_MAGIC,
1548+
"Index magic number not found at end of file"
1549+
);
1550+
1551+
// Verify we can read the index size
1552+
assert!(bytes.len() >= 16, "File is too short to contain index size");
1553+
let size_offset = bytes.len() - 16;
1554+
let index_size = LittleEndian::read_u64(&bytes[size_offset..size_offset + 8]);
1555+
assert!(index_size > 0, "Index size should be greater than 0");
1556+
1557+
// Verify the index size makes sense (should be less than total file size)
1558+
assert!(
1559+
index_size < bytes.len() as u64,
1560+
"Index size is larger than file"
1561+
);
1562+
1563+
Ok(())
1564+
}
1565+
1566+
#[test]
1567+
fn test_finish_idempotent() -> super::Result<()> {
1568+
use crate::vbq::index::INDEX_END_MAGIC;
1569+
use byteorder::{ByteOrder, LittleEndian};
1570+
1571+
// Create a writer
1572+
let header = FileHeaderBuilder::new().build();
1573+
let mut writer = WriterBuilder::default().header(header).build(Vec::new())?;
1574+
1575+
// Write some records
1576+
for i in 0..10 {
1577+
let record = SequencingRecordBuilder::default()
1578+
.s_seq(b"ACGTACGTACGT")
1579+
.flag(i)
1580+
.build()?;
1581+
writer.push(record)?;
1582+
}
1583+
1584+
// Call finish() multiple times
1585+
writer.finish()?;
1586+
let size_after_first_finish = writer.inner.len();
1587+
1588+
writer.finish()?;
1589+
let size_after_second_finish = writer.inner.len();
1590+
1591+
writer.finish()?;
1592+
let size_after_third_finish = writer.inner.len();
1593+
1594+
// All sizes should be the same - index should only be written once
1595+
assert_eq!(size_after_first_finish, size_after_second_finish);
1596+
assert_eq!(size_after_second_finish, size_after_third_finish);
1597+
1598+
// Verify only one index magic number at the end
1599+
let bytes = &writer.inner;
1600+
let magic_offset = bytes.len() - 8;
1601+
let magic = LittleEndian::read_u64(&bytes[magic_offset..]);
1602+
assert_eq!(magic, INDEX_END_MAGIC);
1603+
1604+
Ok(())
1605+
}
15141606
}

0 commit comments

Comments
 (0)