Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions scripts/test_fastq_parse_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env python3
"""Run: python3 scripts/test_fastq_parse_errors.py ./fastp."""
import gzip
import pathlib
import subprocess
import sys
import tempfile


def main():
exe = str(pathlib.Path(sys.argv[1]).resolve())
record = b'@valid\nACGTACGTACGTACGTACGT\n+\nIIIIIIIIIIIIIIIIIIII\n'
# Place errors beyond initial sampling as well as at the first record.
cases = [
('valid', record*4, None),
('no_final_newline', (record*4).rstrip(b'\n'), None),
('invalid_separator', b'@bad\nACGT\nwrong\nIIII\n', 'Invalid FASTQ separator'),
('missing_separator', b'@bad\nACGT\n', 'Invalid FASTQ separator'),
('short_quality', b'@bad\nACGT\n+\nIII\n', 'FASTQ sequence/quality length mismatch'),
('long_quality', b'@bad\nACGT\n+\nIIIII\n', 'FASTQ sequence/quality length mismatch'),
('missing_quality', b'@bad\nACGT\n+\n', 'FASTQ sequence/quality length mismatch'),
]
failures = 0
with tempfile.TemporaryDirectory(prefix='fastp-parse-test-') as tmp:
root = pathlib.Path(tmp)
for zipped in (False, True):
for name, data, expected_error in cases:
for late in ((False, True) if expected_error else (False,)):
label = '%s-%s-%s' % (name, zipped, late)
content = record*40000 + data if late else data
source = root/(label + ('.fq.gz' if zipped else '.fq'))
output = root/(label+'.out.fq')
source.write_bytes(gzip.compress(content) if zipped else content)
cmd = [exe, '-i', str(source), '-o', str(output), '-w', '2',
'-A', '-Q', '-L', '-G', '--dont_eval_duplication',
'-j', '/dev/null', '-h', '/dev/null']
try:
run = subprocess.run(cmd, capture_output=True, timeout=30)
if expected_error:
ok = (run.returncode > 0 and
expected_error.encode() in run.stderr and
str(source).encode() in run.stderr)
else:
ok = (run.returncode == 0 and output.exists() and
output.read_bytes() == content.rstrip(b'\n')+b'\n')
detail = 'exit=%d' % run.returncode
except subprocess.TimeoutExpired:
ok, detail = False, 'timeout'
print('%s %s %s' % ('PASS' if ok else 'FAIL', label, detail))
failures += not ok
return bool(failures)


if __name__ == '__main__':
sys.exit(main())
6 changes: 2 additions & 4 deletions src/fastqreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,7 @@ Read* FastqReader::read(){
if (strand->empty() || (*strand)[0]!='+') {
cerr << *name << endl;
cerr << "Expected '+', got " << *strand << endl;
cerr << "Your FASTQ may be invalid, please check the tail of your FASTQ file" << endl;
return NULL;
error_exit("Invalid FASTQ separator in: " + mFilename);
}

if(quality->length() != sequence->length()) {
Expand All @@ -357,8 +356,7 @@ Read* FastqReader::read(){
cerr << *sequence << endl;
cerr << *strand << endl;
cerr << *quality << endl;
cerr << "Your FASTQ may be invalid, please check the tail of your FASTQ file" << endl;
return NULL;
error_exit("FASTQ sequence/quality length mismatch in: " + mFilename);
}

if(readInPool)
Expand Down