diff --git a/src/lib/demux.rs b/src/lib/demux.rs index ba7ba3b..144eed0 100644 --- a/src/lib/demux.rs +++ b/src/lib/demux.rs @@ -421,6 +421,20 @@ where let mut output_reads = vec![]; let mut qual_counter = BaseQualCounter::new(); + // Ensure that all the reads have the same read name + ensure!(!zipped_reads.is_empty(), "Bug: no records found"); + let first_read_name = + zipped_reads[0].head().splitn(2, |c| *c == b' ').next().unwrap_or(b""); + for read in &zipped_reads { + let cur_read_name = read.head().splitn(2, |c| *c == b' ').next().unwrap_or(b""); + ensure!( + first_read_name == cur_read_name, + "Read names did not match: {:?} != {:?}", + String::from_utf8_lossy(first_read_name), + String::from_utf8_lossy(cur_read_name) + ); + } + // If any filtering was specified, check it here by peeking at the header of the first read in the set of reads. match self.apply_filters(&zipped_reads)? { Some(ReadFailedFilterResult::ControlFilter) => { @@ -1655,4 +1669,28 @@ mod test { "Sample2 R2 reads should match expected" ); } + + #[rstest] + #[rustfmt::skip] + fn test_demux_paired_reads_different_read_names( + #[values(MatcherKind::CachedHammingDistance, MatcherKind::PreCompute)] + matcher: MatcherKind + ) { + let read_structures = vec![ + ReadStructure::from_str("8B100T").unwrap(), + ReadStructure::from_str("9B100T").unwrap(), + ]; + + let mut fq1: OwnedRecord= Fq { name: "frag1", bases: &[&SAMPLE_BARCODE_1[0..8], &[b'A'; 100]].concat(), ..Fq::default() }.to_owned_record(); + let fq2 = Fq { name: "frag2", bases: &[&SAMPLE_BARCODE_1[8..], &[b'T'; 100]].concat(), ..Fq::default() }.to_owned_record(); + + // mess with the FQ1 read name + fq1.head = vec![b'f']; + fq1.head.append(&mut fq2.head.clone()); + + let context = DemuxTestContext::demux_structures(vec![fq1, fq2], read_structures, matcher); + let demuxer = context.demux(); + let result = demuxer.demultiplex(&context.per_fastq_record_set); + assert!(result.is_err()); + } }