diff --git a/public/java/src/org/broadinstitute/sting/utils/locusiterator/AlignmentStateMachine.java b/public/java/src/org/broadinstitute/sting/utils/locusiterator/AlignmentStateMachine.java index 07e885f36..1ea8c6a2c 100644 --- a/public/java/src/org/broadinstitute/sting/utils/locusiterator/AlignmentStateMachine.java +++ b/public/java/src/org/broadinstitute/sting/utils/locusiterator/AlignmentStateMachine.java @@ -25,6 +25,9 @@ package org.broadinstitute.sting.utils.locusiterator; +import com.google.java.contract.Ensures; +import com.google.java.contract.Invariant; +import com.google.java.contract.Requires; import net.sf.samtools.Cigar; import net.sf.samtools.CigarElement; import net.sf.samtools.CigarOperator; @@ -40,16 +43,18 @@ import org.broadinstitute.sting.utils.exceptions.UserException; * implements the traversal along the reference; thus stepForwardOnGenome() returns * on every and only on actual reference bases. This can be a (mis)match or a deletion * (in the latter case, we still return on every individual reference base the deletion spans). - * In the extended events mode, the record state also remembers if there was an insertion, or - * if the deletion just started *right before* the current reference base the record state is - * pointing to upon the return from stepForwardOnGenome(). The next call to stepForwardOnGenome() - * will clear that memory (as we remember only extended events immediately preceding - * the current reference base). * * User: depristo * Date: 1/5/13 * Time: 1:08 PM */ +@Invariant({ + "nCigarElements >= 0", + "cigar != null", + "read != null", + "currentCigarElementOffset >= -1", + "currentCigarElementOffset <= nCigarElements" +}) class AlignmentStateMachine { /** * Our read @@ -79,6 +84,7 @@ class AlignmentStateMachine { */ private int offsetIntoCurrentCigarElement; + @Requires({"read != null", "read.getAlignmentStart() != -1", "read.getCigar() != null"}) public AlignmentStateMachine(final SAMRecord read) { this.read = read; this.cigar = read.getCigar(); @@ -86,28 +92,48 @@ class AlignmentStateMachine { initializeAsLeftEdge(); } + /** + * Initialize the state variables to put this machine one bp before the + * start of the alignment, so that a call to stepForwardOnGenome() will advance + * us to the first proper location + */ + @Ensures("isLeftEdge()") private void initializeAsLeftEdge() { readOffset = offsetIntoCurrentCigarElement = genomeOffset = -1; currentElement = null; } + /** + * Get the read we are aligning to the genome + * @return a non-null GATKSAMRecord + */ + @Ensures("result != null") public SAMRecord getRead() { return read; } /** - * Is this an edge state? I.e., one that is before or after the current read? + * Is this the left edge state? I.e., one that is before or after the current read? * @return true if this state is an edge state, false otherwise */ - public boolean isEdge() { + public boolean isLeftEdge() { return readOffset == -1; } + /** + * Are we on the right edge? I.e., is the current state off the right of the alignment? + * @return true if off the right edge, false if otherwise + */ + public boolean isRightEdge() { + return readOffset == read.getReadLength(); + } + /** * What is our current offset in the read's bases that aligns us with the reference genome? * - * @return the current read offset position + * @return the current read offset position. If an edge will be == -1 */ + @Ensures("result >= -1") public int getReadOffset() { return readOffset; } @@ -115,39 +141,96 @@ class AlignmentStateMachine { /** * What is the current offset w.r.t. the alignment state that aligns us to the readOffset? * - * @return the current offset + * @return the current offset from the alignment start on the genome. If this state is + * at the left edge the result will be -1; */ + @Ensures("result >= -1") public int getGenomeOffset() { return genomeOffset; } + /** + * Get the position (1-based as standard) of the current alignment on the genome w.r.t. the read's alignment start + * @return the position on the genome of the current state in absolute coordinates + */ + @Ensures("result > 0") public int getGenomePosition() { return read.getAlignmentStart() + getGenomeOffset(); } + /** + * Gets #getGenomePosition but as a 1 bp GenomeLoc + * @param genomeLocParser the parser to use to create the genome loc + * @return a non-null genome location with start position of getGenomePosition + */ + @Requires("genomeLocParser != null") + @Ensures("result != null") public GenomeLoc getLocation(final GenomeLocParser genomeLocParser) { + // TODO -- may return wonky results if on an edge (could be 0 or could be beyond genome location) return genomeLocParser.createGenomeLoc(read.getReferenceName(), getGenomePosition()); } + /** + * Get the cigar element we're currently aligning with. + * + * For example, if the cigar string is 2M2D2M and we're in the second step of the + * first 2M, then this function returns the element 2M. After calling stepForwardOnGenome + * this function would return 2D. + * + * @return the cigar element, or null if we're the left edge + */ + @Ensures("result != null || isLeftEdge() || isRightEdge()") public CigarElement getCurrentCigarElement() { return currentElement; } + /** + * Get the offset of the current cigar element among all cigar elements in the read + * + * Suppose our read's cigar is 1M2D3M, and we're at the first 1M. This would + * return 0. Stepping forward puts us in the 2D, so our offset is 1. Another + * step forward would result in a 1 again (we're in the second position of the 2D). + * Finally, one more step forward brings us to 2 (for the 3M element) + * + * @return the offset of the current cigar element in the reads's cigar. Will return -1 for + * when the state is on the left edge, and be == the number of cigar elements in the + * read when we're past the last position on the genome + */ + @Ensures({"result >= -1", "result <= nCigarElements"}) public int getCurrentCigarElementOffset() { return currentCigarElementOffset; } + /** + * Get the offset of the current state into the current cigar element + * + * That is, suppose we have a read with cigar 2M3D4M, and we're right at + * the second M position. offsetIntoCurrentCigarElement would be 1, as + * it's two elements into the 2M cigar. Now stepping forward we'd be + * in cigar element 3D, and our offsetIntoCurrentCigarElement would be 0. + * + * @return the offset (from 0) of the current state in the current cigar element. + * Will be 0 on the right edge, and -1 on the left. + */ + @Ensures({"result >= 0 || (result == -1 && isLeftEdge())", "!isRightEdge() || result == 0"}) public int getOffsetIntoCurrentCigarElement() { return offsetIntoCurrentCigarElement; } /** + * Convenience accessor of the CigarOperator of the current cigar element + * + * Robust to the case where we're on the edge, and currentElement is null, in which + * case this function returns null as well + * * @return null if this is an edge state */ + @Ensures("result != null || isLeftEdge() || isRightEdge()") public CigarOperator getCigarOperator() { return currentElement == null ? null : currentElement.getOperator(); } + @Override public String toString() { return String.format("%s ro=%d go=%d cec=%d %s", read.getReadName(), readOffset, genomeOffset, offsetIntoCurrentCigarElement, currentElement); } @@ -158,6 +241,29 @@ class AlignmentStateMachine { // // ----------------------------------------------------------------------------------------------- + /** + * Step the state machine forward one unit + * + * Takes the current state of this machine, and advances the state until the next on-genome + * cigar element (M, X, =, D) is encountered, at which point this function returns with the + * cigar operator of the current element. + * + * Assumes that the AlignmentStateMachine is in the left edge state at the start, so that + * stepForwardOnGenome() can be called to move the machine to the first alignment position. That + * is, the normal use of this code is: + * + * AlignmentStateMachine machine = new AlignmentStateMachine(read) + * machine.stepForwardOnGenome() + * // now the machine is at the first position on the genome + * + * When stepForwardOnGenome() advances off the right edge of the read, the state machine is + * left in a state such that isRightEdge() returns true and returns null, indicating the + * the machine cannot advance further. The machine may explode, though this is not contracted, + * if stepForwardOnGenome() is called after a previous call returned null. + * + * @return the operator of the cigar element that machine stopped at, null if we advanced off the end of the read + */ + @Ensures("result != null || isRightEdge()") public CigarOperator stepForwardOnGenome() { // loop until we either find a cigar element step that moves us one base on the genome, or we run // out of cigar elements @@ -177,11 +283,17 @@ class AlignmentStateMachine { if (currentElement != null && currentElement.getOperator() == CigarOperator.D) throw new UserException.MalformedBAM(read, "read ends with deletion. Cigar: " + read.getCigarString() + ". Although the SAM spec technically permits such reads, this is often indicative of malformed files. If you are sure you want to use this file, re-run your analysis with the extra option: -rf BadCigar"); + // we're done, so set the offset of the cigar to 0 for cleanliness, as well as the current element + offsetIntoCurrentCigarElement = 0; + readOffset = read.getReadLength(); + currentElement = null; + // Reads that contain indels model the genomeOffset as the following base in the reference. Because // we fall into this else block only when indels end the read, increment genomeOffset such that the // current offset of this read is the next ref base after the end of the indel. This position will // model a point on the reference somewhere after the end of the read. genomeOffset++; // extended events need that. Logically, it's legal to advance the genomic offset here: + // we do step forward on the ref, and by returning null we also indicate that we are past the read end. return null; } diff --git a/public/java/src/org/broadinstitute/sting/utils/locusiterator/LIBSDownsamplingInfo.java b/public/java/src/org/broadinstitute/sting/utils/locusiterator/LIBSDownsamplingInfo.java index 1783fa1de..fc4a5a7eb 100644 --- a/public/java/src/org/broadinstitute/sting/utils/locusiterator/LIBSDownsamplingInfo.java +++ b/public/java/src/org/broadinstitute/sting/utils/locusiterator/LIBSDownsamplingInfo.java @@ -26,12 +26,12 @@ package org.broadinstitute.sting.utils.locusiterator; /** -* Created with IntelliJ IDEA. -* User: depristo -* Date: 1/5/13 -* Time: 1:26 PM -* To change this template use File | Settings | File Templates. -*/ + * Simple wrapper about the information LIBS needs about downsampling + * + * User: depristo + * Date: 1/5/13 + * Time: 1:26 PM + */ public class LIBSDownsamplingInfo { public final static LIBSDownsamplingInfo NO_DOWNSAMPLING = new LIBSDownsamplingInfo(false, -1); diff --git a/public/java/src/org/broadinstitute/sting/utils/locusiterator/LocusIteratorByState.java b/public/java/src/org/broadinstitute/sting/utils/locusiterator/LocusIteratorByState.java index f67b09098..e2f05efcf 100644 --- a/public/java/src/org/broadinstitute/sting/utils/locusiterator/LocusIteratorByState.java +++ b/public/java/src/org/broadinstitute/sting/utils/locusiterator/LocusIteratorByState.java @@ -52,6 +52,7 @@ package org.broadinstitute.sting.utils.locusiterator; import com.google.java.contract.Ensures; +import com.google.java.contract.Requires; import net.sf.samtools.CigarOperator; import net.sf.samtools.SAMRecord; import org.apache.log4j.Logger; @@ -69,12 +70,16 @@ import java.util.*; /** * Iterator that traverses a SAM File, accumulating information on a per-locus basis + * + * Produces AlignmentContext objects, that contain ReadBackedPileups of PileupElements. This + * class has its core job of converting an iterator of ordered SAMRecords into those + * RBPs. */ public class LocusIteratorByState extends LocusIterator { /** * our log, which we want to capture anything from this class */ - private static Logger logger = Logger.getLogger(LocusIteratorByState.class); + private final static Logger logger = Logger.getLogger(LocusIteratorByState.class); // ----------------------------------------------------------------------------------------------------------------- // @@ -83,13 +88,32 @@ public class LocusIteratorByState extends LocusIterator { // ----------------------------------------------------------------------------------------------------------------- /** - * Used to create new GenomeLocs. + * Used to create new GenomeLocs as needed */ private final GenomeLocParser genomeLocParser; + + /** + * A complete list of all samples that may come out of the reads. Must be + * comprehensive. + */ private final ArrayList samples; + + /** + * The system that maps incoming reads from the iterator to their pileup states + */ private final ReadStateManager readStates; + + /** + * Should we include reads in the pileup which are aligned with a deletion operator to the reference? + */ private final boolean includeReadsWithDeletionAtLoci; + /** + * The next alignment context. A non-null value means that a + * context is waiting from hasNext() for sending off to the next next() call. A null + * value means that either hasNext() has not been called at all or that + * the underlying iterator is exhausted + */ private AlignmentContext nextAlignmentContext; // ----------------------------------------------------------------------------------------------------------------- @@ -98,6 +122,18 @@ public class LocusIteratorByState extends LocusIterator { // // ----------------------------------------------------------------------------------------------------------------- + /** + * Create a new LocusIteratorByState + * + * @param samIterator the iterator of reads to process into pileups. Reads must be ordered + * according to standard coordinate-sorted BAM conventions + * @param readInformation meta-information about how to process the reads (i.e., should we do downsampling?) + * @param genomeLocParser used to create genome locs + * @param samples a complete list of samples present in the read groups for the reads coming from samIterator. + * This is generally just the set of read group sample fields in the SAMFileHeader. This + * list of samples may contain a null element, and all reads without read groups will + * be mapped to this null sample + */ public LocusIteratorByState(final Iterator samIterator, final ReadProperties readInformation, final GenomeLocParser genomeLocParser, @@ -116,16 +152,21 @@ public class LocusIteratorByState extends LocusIterator { final GenomeLocParser genomeLocParser, final Collection samples, final boolean maintainUniqueReadsList) { + if ( samIterator == null ) throw new IllegalArgumentException("samIterator cannot be null"); + if ( downsamplingInfo == null ) throw new IllegalArgumentException("downsamplingInfo cannot be null"); + if ( genomeLocParser == null ) throw new IllegalArgumentException("genomeLocParser cannot be null"); + if ( samples == null ) throw new IllegalArgumentException("Samples cannot be null"); + + // currently the GATK expects this LocusIteratorByState to accept empty sample lists, when + // there's no read data. So we need to throw this error only when samIterator.hasNext() is true + if (samples.isEmpty() && samIterator.hasNext()) { + throw new IllegalArgumentException("samples list must not be empty"); + } + this.genomeLocParser = genomeLocParser; this.includeReadsWithDeletionAtLoci = includeReadsWithDeletionAtLoci; this.samples = new ArrayList(samples); this.readStates = new ReadStateManager(samIterator, this.samples, downsamplingInfo, maintainUniqueReadsList); - - // currently the GATK expects this LocusIteratorByState to accept empty sample lists, when - // there's no read data. So we need to throw this error only when samIterator.hasNext() is true - if (this.samples.isEmpty() && samIterator.hasNext()) { - throw new IllegalArgumentException("samples list must not be empty"); - } } @Override @@ -133,16 +174,14 @@ public class LocusIteratorByState extends LocusIterator { return this; } - @Override - public void close() { - } - - @Override - public boolean hasNext() { - lazyLoadNextAlignmentContext(); - return nextAlignmentContext != null; - } - + /** + * Get the current location (i.e., the bp of the center of the pileup) of the pileup, or null if not anywhere yet + * + * Assumes that read states is updated to reflect the current pileup position, but not advanced to the + * next location. + * + * @return the location of the current pileup, or null if we're after all reads + */ private GenomeLoc getLocation() { return readStates.isEmpty() ? null : readStates.getFirst().getLocation(genomeLocParser); } @@ -153,6 +192,22 @@ public class LocusIteratorByState extends LocusIterator { // // ----------------------------------------------------------------------------------------------------------------- + /** + * Is there another pileup available? + * @return + */ + @Override + public boolean hasNext() { + lazyLoadNextAlignmentContext(); + return nextAlignmentContext != null; + } + + /** + * Get the next AlignmentContext available from the reads. + * + * @return a non-null AlignmentContext of the pileup after to the next genomic position covered by + * at least one read. + */ @Override public AlignmentContext next() { lazyLoadNextAlignmentContext(); @@ -164,8 +219,9 @@ public class LocusIteratorByState extends LocusIterator { } /** - * Creates the next alignment context from the given state. Note that this is implemented as a lazy load method. - * nextAlignmentContext MUST BE null in order for this method to advance to the next entry. + * Creates the next alignment context from the given state. Note that this is implemented as a + * lazy load method. nextAlignmentContext MUST BE null in order for this method to advance to the + * next entry. */ private void lazyLoadNextAlignmentContext() { while (nextAlignmentContext == null && readStates.hasNext()) { @@ -193,7 +249,7 @@ public class LocusIteratorByState extends LocusIterator { if (op == CigarOperator.N) // N's are never added to any pileup continue; - if (!filterBaseInRead(read, location.getStart())) { + if (!dontIncludeReadInPileup(read, location.getStart())) { if ( op == CigarOperator.D ) { if ( ! includeReadsWithDeletionAtLoci ) continue; @@ -220,6 +276,10 @@ public class LocusIteratorByState extends LocusIterator { } } + /** + * Advances all fo the read states by one bp. After this call the read states are reflective + * of the next pileup. + */ private void updateReadStates() { for (final String sample : samples) { Iterator it = readStates.iterator(sample); @@ -288,13 +348,16 @@ public class LocusIteratorByState extends LocusIterator { // ----------------------------------------------------------------------------------------------------------------- /** + * Should this read be excluded from the pileup? + * * Generic place to put per-base filters appropriate to LocusIteratorByState * - * @param rec - * @param pos - * @return + * @param rec the read to potentially exclude + * @param pos the genomic position of the current alignment + * @return true if the read should be excluded from the pileup, false otherwise */ - private boolean filterBaseInRead(GATKSAMRecord rec, long pos) { + @Requires({"rec != null", "pos > 0"}) + private boolean dontIncludeReadInPileup(GATKSAMRecord rec, long pos) { return ReadUtils.isBaseInsideAdaptor(rec, pos); } @@ -311,6 +374,8 @@ public class LocusIteratorByState extends LocusIterator { * @param readInfo GATK engine information about what should be done to the reads * @return a LIBS specific info holder about downsampling only */ + @Requires("readInfo != null") + @Ensures("result != null") private static LIBSDownsamplingInfo toDownsamplingInfo(final ReadProperties readInfo) { final boolean performDownsampling = readInfo.getDownsamplingMethod() != null && readInfo.getDownsamplingMethod().type == DownsampleType.BY_SAMPLE && diff --git a/public/java/test/org/broadinstitute/sting/utils/locusiterator/AlignmentStateMachineUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/locusiterator/AlignmentStateMachineUnitTest.java index 4e2c55a8c..85f8be905 100644 --- a/public/java/test/org/broadinstitute/sting/utils/locusiterator/AlignmentStateMachineUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/locusiterator/AlignmentStateMachineUnitTest.java @@ -41,7 +41,7 @@ public class AlignmentStateMachineUnitTest extends LocusIteratorByStateBaseTest // return new Object[][]{{new LIBSTest("2M2D2X", 2)}}; // return createLIBSTests( // Arrays.asList(2), -// Arrays.asList(5)); +// Arrays.asList(2)); return createLIBSTests( Arrays.asList(1, 2), Arrays.asList(1, 2, 3, 4)); @@ -63,15 +63,23 @@ public class AlignmentStateMachineUnitTest extends LocusIteratorByStateBaseTest int lastOffset = -1; // TODO -- more tests about test state machine state before first step? - Assert.assertTrue(state.isEdge()); + Assert.assertTrue(state.isLeftEdge()); + Assert.assertNull(state.getCigarOperator()); + Assert.assertNotNull(state.toString()); + Assert.assertEquals(state.getReadOffset(), -1); + Assert.assertEquals(state.getGenomeOffset(), -1); + Assert.assertEquals(state.getCurrentCigarElementOffset(), -1); + Assert.assertEquals(state.getCurrentCigarElement(), null); while ( state.stepForwardOnGenome() != null ) { + Assert.assertNotNull(state.toString()); + tester.stepForwardOnGenome(); Assert.assertTrue(state.getReadOffset() >= lastOffset, "Somehow read offsets are decreasing: lastOffset " + lastOffset + " current " + state.getReadOffset()); Assert.assertEquals(state.getReadOffset(), tester.getCurrentReadOffset(), "Read offsets are wrong at " + bpVisited); - Assert.assertFalse(state.isEdge()); + Assert.assertFalse(state.isLeftEdge()); Assert.assertEquals(state.getCurrentCigarElement(), read.getCigar().getCigarElement(tester.currentOperatorIndex), "CigarElement index failure"); Assert.assertEquals(state.getOffsetIntoCurrentCigarElement(), tester.getCurrentPositionOnOperatorBase0(), "CigarElement index failure"); @@ -91,5 +99,9 @@ public class AlignmentStateMachineUnitTest extends LocusIteratorByStateBaseTest } Assert.assertEquals(bpVisited, expectedBpToVisit, "Didn't visit the expected number of bp"); + Assert.assertEquals(state.getReadOffset(), read.getReadLength()); + Assert.assertEquals(state.getCurrentCigarElementOffset(), read.getCigarLength()); + Assert.assertEquals(state.getCurrentCigarElement(), null); + Assert.assertNotNull(state.toString()); } } diff --git a/public/java/test/org/broadinstitute/sting/utils/locusiterator/LocusIteratorByStateBaseTest.java b/public/java/test/org/broadinstitute/sting/utils/locusiterator/LocusIteratorByStateBaseTest.java index 7453267df..a23ea28e6 100644 --- a/public/java/test/org/broadinstitute/sting/utils/locusiterator/LocusIteratorByStateBaseTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/locusiterator/LocusIteratorByStateBaseTest.java @@ -90,7 +90,7 @@ public class LocusIteratorByStateBaseTest extends BaseTest { new ValidationExclusion(), Collections.emptyList(), Collections.emptyList(), - false, + true, (byte) -1, keepReads); } diff --git a/public/java/test/org/broadinstitute/sting/utils/locusiterator/LocusIteratorByStateUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/locusiterator/LocusIteratorByStateUnitTest.java index ec817b65c..688de70c0 100644 --- a/public/java/test/org/broadinstitute/sting/utils/locusiterator/LocusIteratorByStateUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/locusiterator/LocusIteratorByStateUnitTest.java @@ -50,9 +50,10 @@ import java.util.*; * testing of the new (non-legacy) version of LocusIteratorByState */ public class LocusIteratorByStateUnitTest extends LocusIteratorByStateBaseTest { + private static final boolean DEBUG = false; protected LocusIteratorByState li; - @Test + @Test(enabled = true && ! DEBUG) public void testXandEQOperators() { final byte[] bases1 = new byte[] {'A','A','A','A','A','A','A','A','A','A'}; final byte[] bases2 = new byte[] {'A','A','A','C','A','A','A','A','A','C'}; @@ -92,7 +93,7 @@ public class LocusIteratorByStateUnitTest extends LocusIteratorByStateBaseTest { } } - @Test(enabled = true) + @Test(enabled = true && ! DEBUG) public void testIndelsInRegularPileup() { final byte[] bases = new byte[] {'A','A','A','A','A','A','A','A','A','A'}; final byte[] indelBases = new byte[] {'A','A','A','A','C','T','A','A','A','A','A','A'}; @@ -138,7 +139,7 @@ public class LocusIteratorByStateUnitTest extends LocusIteratorByStateBaseTest { Assert.assertTrue(foundIndel,"Indel in pileup not found"); } - @Test(enabled = false) + @Test(enabled = false && ! DEBUG) public void testWholeIndelReadInIsolation() { final int firstLocus = 44367789; @@ -169,7 +170,7 @@ public class LocusIteratorByStateUnitTest extends LocusIteratorByStateBaseTest { * Test to make sure that reads supporting only an indel (example cigar string: 76I) do * not negatively influence the ordering of the pileup. */ - @Test(enabled = true) + @Test(enabled = true && ! DEBUG) public void testWholeIndelRead() { final int firstLocus = 44367788, secondLocus = firstLocus + 1; @@ -220,7 +221,7 @@ public class LocusIteratorByStateUnitTest extends LocusIteratorByStateBaseTest { /** * Test to make sure that reads supporting only an indel (example cigar string: 76I) are represented properly */ - @Test(enabled = false) + @Test(enabled = false && ! DEBUG) public void testWholeIndelReadRepresentedTest() { final int firstLocus = 44367788, secondLocus = firstLocus + 1; @@ -298,7 +299,7 @@ public class LocusIteratorByStateUnitTest extends LocusIteratorByStateBaseTest { return tests.toArray(new Object[][]{}); } - @Test(dataProvider = "IndelLengthAndBasesTest") + @Test(enabled = true && ! DEBUG, dataProvider = "IndelLengthAndBasesTest") public void testIndelLengthAndBasesTest(GATKSAMRecord read, final CigarOperator op, final int eventSize, final String eventBases) { // create the iterator by state with the fake reads and fake records li = makeLTBS(Arrays.asList((SAMRecord)read), createTestReadProperties()); @@ -337,7 +338,7 @@ public class LocusIteratorByStateUnitTest extends LocusIteratorByStateBaseTest { public Object[][] makeLIBSTest() { final List tests = new LinkedList(); -// tests.add(new Object[]{new LIBSTest("1X2D2P2X", 1)}); +// tests.add(new Object[]{new LIBSTest("2=2D2=2X", 1)}); // return tests.toArray(new Object[][]{}); return createLIBSTests( @@ -349,7 +350,7 @@ public class LocusIteratorByStateUnitTest extends LocusIteratorByStateBaseTest { // Arrays.asList(3)); } - @Test(enabled = false, dataProvider = "LIBSTest") + @Test(enabled = true, dataProvider = "LIBSTest") public void testLIBS(LIBSTest params) { // create the iterator by state with the fake reads and fake records final GATKSAMRecord read = params.makeRead(); @@ -366,19 +367,19 @@ public class LocusIteratorByStateUnitTest extends LocusIteratorByStateBaseTest { Assert.assertEquals(p.getNumberOfElements(), 1); PileupElement pe = p.iterator().next(); - Assert.assertEquals(p.getNumberOfDeletions(), pe.isDeletion() ? 1 : 0); - Assert.assertEquals(p.getNumberOfMappingQualityZeroReads(), pe.getRead().getMappingQuality() == 0 ? 1 : 0); + Assert.assertEquals(p.getNumberOfDeletions(), pe.isDeletion() ? 1 : 0, "wrong number of deletions in the pileup"); + Assert.assertEquals(p.getNumberOfMappingQualityZeroReads(), pe.getRead().getMappingQuality() == 0 ? 1 : 0, "wront number of mapq reads in the pileup"); tester.stepForwardOnGenome(); if ( ! hasNeighboringPaddedOps(params.getElements(), pe.getCurrentCigarOffset()) ) { - Assert.assertEquals(pe.isBeforeDeletionStart(), tester.isBeforeDeletionStart); - Assert.assertEquals(pe.isAfterDeletionEnd(), tester.isAfterDeletionEnd); + Assert.assertEquals(pe.isBeforeDeletionStart(), tester.isBeforeDeletionStart, "before deletion start failure"); + Assert.assertEquals(pe.isAfterDeletionEnd(), tester.isAfterDeletionEnd, "after deletion end failure"); } - Assert.assertEquals(pe.isBeforeInsertion(), tester.isBeforeInsertion); - Assert.assertEquals(pe.isAfterInsertion(), tester.isAfterInsertion); - Assert.assertEquals(pe.isNextToSoftClip(), tester.isNextToSoftClip); + Assert.assertEquals(pe.isBeforeInsertion(), tester.isBeforeInsertion, "before insertion failure"); + Assert.assertEquals(pe.isAfterInsertion(), tester.isAfterInsertion, "after insertion failure"); + Assert.assertEquals(pe.isNextToSoftClip(), tester.isNextToSoftClip, "next to soft clip failure"); Assert.assertTrue(pe.getOffset() >= lastOffset, "Somehow read offsets are decreasing: lastOffset " + lastOffset + " current " + pe.getOffset()); Assert.assertEquals(pe.getOffset(), tester.getCurrentReadOffset(), "Read offsets are wrong at " + bpVisited); @@ -391,7 +392,7 @@ public class LocusIteratorByStateUnitTest extends LocusIteratorByStateBaseTest { Assert.assertTrue(pe.getOffsetInCurrentCigar() >= 0, "Offset into current cigar too small"); Assert.assertTrue(pe.getOffsetInCurrentCigar() < pe.getCurrentCigarElement().getLength(), "Offset into current cigar too big"); - Assert.assertEquals(pe.getOffset(), tester.getCurrentReadOffset()); + Assert.assertEquals(pe.getOffset(), tester.getCurrentReadOffset(), "Read offset failure"); lastOffset = pe.getOffset(); } @@ -431,7 +432,7 @@ public class LocusIteratorByStateUnitTest extends LocusIteratorByStateBaseTest { return tests.toArray(new Object[][]{}); } - @Test(enabled = true, dataProvider = "LIBSKeepSubmittedReads") + @Test(enabled = true && ! DEBUG, dataProvider = "LIBSKeepSubmittedReads") public void testLIBSKeepSubmittedReads(final int nReadsPerLocus, final int nLoci, final int nSamples,