// Copyright (C) 2011-2012 CRS4.
//
// This file is part of Seal.
//
// Seal is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// Seal is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with Seal. If not, see <http://www.gnu.org/licenses/>.
package tests.it.crs4.seal.common;
import it.crs4.seal.common.AbstractSamMapping;
import it.crs4.seal.common.AlignOp;
import it.crs4.seal.common.AlignOp.Type;
import it.crs4.seal.common.FormatException;
import java.util.List;
import java.util.ArrayList;
import java.nio.ByteBuffer;
import org.junit.*;
import static org.junit.Assert.*;
public class TestAbstractSamMapping
{
private static final String sam = "ERR020229.100000/1 89 chr6 3558357 37 91M = 3558678 400 AGCTTCTTTGACTCTCGAATTTTAGCACTAGAAGAAATAGTGAGGATTATATATTTCAGAAGTTCTCACCCAGGATATCAGAACACATTCA 5:CB:CCBCCB>:C@;BBBB??B;?>1@@=C=4ACCAB3A8=CC=C?CBC=CBCCCCCCCCCCCCC@5>?=?CAAB=3=>====5>=AC?C XT:A:U NM:i:0 SM:i:37 AM:i:0 X0:i:1 X1:i:0 XM:i:0 XO:i:0 XG:i:0 MD:Z:91";
private static final String samRead2 = "ERR020229.11100 163 chr2 207301655 60 91M = 207302028 464 CACCCAAGAAATGTGTTGAATAAATGAATCAGGAGAGGCTGGTTAGCACTGTGCAGGGAGAGTGCCTTGCCTGTGATCTCTGCCAGTCGAC GGGGGGGGFGGGGGGGGGGFGGGGGGGFGGGGGGGGGGG?EE5?=16450A?A@:9<A################################# XT:A:U NM:i:0 SM:i:37 AM:i:37 X0:i:1 X1:i:0 XM:i:0 XO:i:0 XG:i:0 MD:Z:91";
private static final String samUnmapped = "ERR020229.11100 79 * * 0 * * * * CACCCAAGAAATGTGTTGAATAAATGAATCAGGAGAGGCTGGTTAGCACTGTGCAGGGAGAGTGCCTTGCCTGTGATCTCTGCCAGTCGAC GGGGGGGGFGGGGGGGGGGFGGGGGGGFGGGGGGGGGGG?EE5?=16450A?A@:9<A#################################";
private static final String insertion = "INSERT 107 chr12 1 60 5M4I8M * * * TGAAGCTATTTAAATTA CFFFFGBGGGGGDGGGB XT:A:U NM:i:4 SM:i:37 AM:i:37 X0:i:1 X1:i:0 XM:i:0 XO:i:1 XG:i:4 MD:Z:17";
private static final String deletion = "DELETE 107 chr12 1 60 5M2D3M * * * TGAAGATT CFFFFGGG XT:A:U NM:i:4 SM:i:37 AM:i:37 X0:i:1 X1:i:0 XM:i:0 XO:i:1 XG:i:4 MD:Z:5^CA3";
private static final String trimmed = "TRIMMED 107 chr12 1 60 4S8M * * * ACGTTGAAGATT BBBBCFFFFGGG XC:i:8 XT:A:U NM:i:4 SM:i:37 AM:i:37 X0:i:1 X1:i:0 XM:i:0 XO:i:1 XG:i:4 MD:Z:8";
private static final String mismatch = "MISMATCH 107 chr12 1 60 8M * * * ACGTTGAA BBBBCFFF XC:i:8 XT:A:U NM:i:4 SM:i:37 AM:i:37 X0:i:1 X1:i:0 XM:i:0 XO:i:1 XG:i:4 MD:Z:3AA2G0";
private static final String missingMD = "MISMATCH 107 chr12 1 60 8M * * * ACGTTGAA BBBBCFFF XC:i:8 XT:A:U NM:i:4 SM:i:37 AM:i:37 X0:i:1 X1:i:0 XM:i:0 XO:i:1 XG:i:4";
@Ignore // tell JUnit not to try to instantiate this class
private static class SimpleSamMapping extends AbstractSamMapping {
private String source;
private String[] fields;
public SimpleSamMapping(String sam) {
source = sam;
fields = source.split("\t");
}
public String getName() { return fields[0]; }
public int getFlag() { return Integer.parseInt(fields[1]); }
public String getContig() { return fields[2]; }
public int get5Position() { return Integer.parseInt(fields[3]); }
public int getMapQ() { return Integer.parseInt(fields[4]); }
public String getCigarStr() { return fields[5]; }
public boolean isTemplateLengthAvailable() { return !("*".equals(fields[8]) || "0".equals(fields[8])); }
public int getTemplateLength() { return Integer.parseInt(fields[8]); }
public ByteBuffer getSequence() { return ByteBuffer.wrap(fields[9].getBytes()); }
public ByteBuffer getBaseQualities() { return ByteBuffer.wrap(fields[10].getBytes()); }
public int getLength() { return fields[9].length(); }
protected String getTagText(String name)
{
for (int i = 11; i < fields.length; ++i)
{
if (fields[i].startsWith(name + ":"))
return fields[i];
}
return null;
}
}
private SimpleSamMapping simpleMapping;
@Before
public void setup()
{
simpleMapping = new SimpleSamMapping(sam);
}
@Test
public void testGetSequenceString()
{
assertEquals("AGCTTCTTTGACTCTCGAATTTTAGCACTAGAAGAAATAGTGAGGATTATATATTTCAGAAGTTCTCACCCAGGATATCAGAACACATTCA", simpleMapping.getSequenceString());
}
@Test
public void testGetBaseQualitiesString()
{
assertEquals("5:CB:CCBCCB>:C@;BBBB??B;?>1@@=C=4ACCAB3A8=CC=C?CBC=CBCCCCCCCCCCCCC@5>?=?CAAB=3=>====5>=AC?C", simpleMapping.getBaseQualitiesString());
}
@Test
public void testSimpleGetAlignment()
{
List<AlignOp> list = simpleMapping.getAlignment();
assertEquals(1, list.size());
AlignOp align = list.get(0);
assertEquals(Type.Match, align.getType());
assertEquals(91, align.getLen());
}
@Test(expected=IllegalStateException.class)
public void testUnmappedGetAlignment()
{
simpleMapping = new SimpleSamMapping(samUnmapped);
simpleMapping.getAlignment();
}
@Test
public void testGetAlignmentElements()
{
String moreSam = "id 99 chr11 1 60 10M = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertEquals(new AlignOp(Type.Match, 10), simpleMapping.getAlignment().get(0));
// repeat to test cached value
assertEquals(new AlignOp(Type.Match, 10), simpleMapping.getAlignment().get(0));
moreSam = "id 99 chr11 1 60 10I = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertEquals(new AlignOp(Type.Insert, 10), simpleMapping.getAlignment().get(0));
moreSam = "id 99 chr11 1 60 10D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertEquals(new AlignOp(Type.Delete, 10), simpleMapping.getAlignment().get(0));
moreSam = "id 99 chr11 1 60 10S = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertEquals(new AlignOp(Type.SoftClip, 10), simpleMapping.getAlignment().get(0));
moreSam = "id 99 chr11 1 60 10H = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertEquals(new AlignOp(Type.HardClip, 10), simpleMapping.getAlignment().get(0));
moreSam = "id 99 chr11 1 60 10N = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertEquals(new AlignOp(Type.Skip, 10), simpleMapping.getAlignment().get(0));
moreSam = "id 99 chr11 1 60 10P = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertEquals(new AlignOp(Type.Pad, 10), simpleMapping.getAlignment().get(0));
}
@Test
public void testComplexGetAlignment()
{
String moreSam = "id 99 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
List<AlignOp> list = simpleMapping.getAlignment();
assertEquals(4, list.size());
AlignOp align = list.get(0);
assertEquals(Type.Match, align.getType());
assertEquals(3, align.getLen());
align = list.get(1);
assertEquals(Type.Insert, align.getType());
assertEquals(1, align.getLen());
align = list.get(2);
assertEquals(Type.Match, align.getType());
assertEquals(5, align.getLen());
align = list.get(3);
assertEquals(Type.Delete, align.getType());
assertEquals(1, align.getLen());
}
@Test(expected=FormatException.class)
public void testInvalidCigar1()
{
String moreSam = "id 99 chr11 1 60 pippo = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
simpleMapping.getAlignment();
}
@Test(expected=FormatException.class)
public void testInvalidCigar2()
{
String moreSam = "id 99 chr11 1 60 10 = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
simpleMapping.getAlignment();
}
@Test(expected=FormatException.class)
public void testInvalidCigar3()
{
String moreSam = "id 99 chr11 1 60 M = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
simpleMapping.getAlignment();
}
@Test
public void testSimpleRefCoordinates()
{
ArrayList<Integer> coordinates = new ArrayList<Integer>(simpleMapping.getLength());
simpleMapping.calculateReferenceCoordinates(coordinates);
assertEquals(simpleMapping.getLength(), coordinates.size());
for (int i = 0; i < simpleMapping.getLength(); ++i)
assertEquals(simpleMapping.get5Position() + i, coordinates.get(i).intValue());
}
@Test
public void testInsertionRefCoordinates()
{
simpleMapping = new SimpleSamMapping(insertion);
ArrayList<Integer> coordinates = new ArrayList<Integer>(simpleMapping.getLength());
simpleMapping.calculateReferenceCoordinates(coordinates);
assertEquals(simpleMapping.getLength(), coordinates.size());
int[] coords = new int[] { 1,2,3,4,5,-1,-1,-1,-1,6,7,8,9,10,11,12,13 };
for (int i = 0; i < coords.length; ++i)
assertEquals(coords[i], coordinates.get(i).intValue());
}
@Test
public void testDeletionRefCoordinates()
{
simpleMapping = new SimpleSamMapping(deletion);
ArrayList<Integer> coordinates = new ArrayList<Integer>(simpleMapping.getLength());
simpleMapping.calculateReferenceCoordinates(coordinates);
assertEquals(simpleMapping.getLength(), coordinates.size());
int[] coords = new int[] { 1,2,3,4,5,8,9,10 };
for (int i = 0; i < coords.length; ++i)
assertEquals(coords[i], coordinates.get(i).intValue());
}
@Test
public void testTrimmedRefCoordinates()
{
simpleMapping = new SimpleSamMapping(trimmed);
ArrayList<Integer> coordinates = new ArrayList<Integer>(simpleMapping.getLength());
simpleMapping.calculateReferenceCoordinates(coordinates);
assertEquals(simpleMapping.getLength(), coordinates.size());
int[] coords = new int[] { -1,-1,-1,-1,1,2,3,4,5,6,7,8 };
for (int i = 0; i < coords.length; ++i)
assertEquals(coords[i], coordinates.get(i).intValue());
}
@Test
public void testSimpleRefMatches()
{
ArrayList<Boolean> matches = new ArrayList<Boolean>(simpleMapping.getLength());
simpleMapping.calculateReferenceMatches(matches);
assertEquals(simpleMapping.getLength(), matches.size());
for (int i = 0; i < simpleMapping.getLength(); ++i)
assertEquals(true, matches.get(i).booleanValue());
}
@Test
public void testInsertRefMatches()
{
simpleMapping = new SimpleSamMapping(insertion);
ArrayList<Boolean> matches = new ArrayList<Boolean>(simpleMapping.getLength());
simpleMapping.calculateReferenceMatches(matches);
assertEquals(simpleMapping.getLength(), matches.size());
Boolean[] expected = new Boolean[] { true,true,true,true,true,null,null,null,null,true,true,true,true,true,true,true,true };
for (int i = 0; i < simpleMapping.getLength(); ++i)
assertEquals(expected[i], matches.get(i));
}
@Test
public void testDeletionRefMatches()
{
simpleMapping = new SimpleSamMapping(deletion);
ArrayList<Boolean> matches = new ArrayList<Boolean>(simpleMapping.getLength());
simpleMapping.calculateReferenceMatches(matches);
assertEquals(simpleMapping.getLength(), matches.size());
Boolean[] expected = new Boolean[] { true,true,true,true,true,true,true,true };
for (int i = 0; i < simpleMapping.getLength(); ++i)
assertEquals(expected[i], matches.get(i));
}
@Test
public void testSoftClipRefMatches()
{
simpleMapping = new SimpleSamMapping(trimmed);
ArrayList<Boolean> matches = new ArrayList<Boolean>(simpleMapping.getLength());
simpleMapping.calculateReferenceMatches(matches);
assertEquals(simpleMapping.getLength(), matches.size());
Boolean[] expected = new Boolean[] { null,null,null,null,true,true,true,true,true,true,true,true };
for (int i = 0; i < simpleMapping.getLength(); ++i)
assertEquals(expected[i], matches.get(i));
}
@Test
public void testMismatchRefMatches()
{
simpleMapping = new SimpleSamMapping(mismatch);
ArrayList<Boolean> matches = new ArrayList<Boolean>(simpleMapping.getLength());
simpleMapping.calculateReferenceMatches(matches);
assertEquals(simpleMapping.getLength(), matches.size());
Boolean[] expected = new Boolean[] { true,true,true,false,false,true,true,false };
for (int i = 0; i < simpleMapping.getLength(); ++i)
assertEquals(expected[i], matches.get(i));
}
@Test(expected=IllegalStateException.class)
public void testUnmappedRefCoordinates()
{
simpleMapping = new SimpleSamMapping(samUnmapped);
ArrayList<Integer> coords = new ArrayList<Integer>(simpleMapping.getLength());
simpleMapping.calculateReferenceCoordinates(coords);
}
@Test(expected=IllegalStateException.class)
public void testUnmappedRefMatches()
{
simpleMapping = new SimpleSamMapping(samUnmapped);
ArrayList<Boolean> matches = new ArrayList<Boolean>(simpleMapping.getLength());
simpleMapping.calculateReferenceMatches(matches);
}
@Test(expected=RuntimeException.class)
public void testMissingMdRefMatches()
{
simpleMapping = new SimpleSamMapping(missingMD);
ArrayList<Boolean> matches = new ArrayList<Boolean>(simpleMapping.getLength());
simpleMapping.calculateReferenceMatches(matches);
}
@Test(expected=RuntimeException.class)
public void testInconsistentMdRefMatches()
{
String sam = missingMD + "\tMD:Z:5M"; // Read is 8 bases long an cigar has an 8-base match
simpleMapping = new SimpleSamMapping(sam);
ArrayList<Boolean> matches = new ArrayList<Boolean>(simpleMapping.getLength());
simpleMapping.calculateReferenceMatches(matches);
}
//
// get*Tag are repeated more than once to exercise the caching mechanism
//
@Test
public void testGetTag() throws NoSuchFieldException
{
assertEquals("91", simpleMapping.getTag("MD"));
assertEquals("91", simpleMapping.getTag("MD"));
assertEquals("U", simpleMapping.getTag("XT"));
assertEquals("37", simpleMapping.getTag("SM"));
}
@Test(expected=NoSuchFieldException.class)
public void testGetInexistantTag() throws NoSuchFieldException
{
simpleMapping.getTag("XX");
}
@Test
public void testHasTag()
{
assertTrue(simpleMapping.hasTag("MD"));
assertFalse(simpleMapping.hasTag("XX"));
}
@Test
public void testIntTag() throws NoSuchFieldException
{
assertEquals(37, simpleMapping.getIntTag("SM"));
assertEquals(37, simpleMapping.getIntTag("SM"));
}
@Test(expected=NumberFormatException.class)
public void testBadIntTag() throws NoSuchFieldException
{
simpleMapping.getIntTag("XT");
}
@Test(expected=NumberFormatException.class)
public void testBadDoubleTag() throws NoSuchFieldException
{
simpleMapping.getDoubleTag("XT");
}
@Test
public void testDoubleTag() throws NoSuchFieldException
{
assertEquals(1, simpleMapping.getDoubleTag("X0"), 0.001);
assertEquals(1, simpleMapping.getDoubleTag("X0"), 0.001);
}
/// flag tests
@Test
public void testPaired()
{
String moreSam = "id 67 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertTrue(simpleMapping.isPaired());
assertTrue(simpleMapping.isProperlyPaired());
moreSam = "id 0 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertFalse(simpleMapping.isPaired());
assertFalse(simpleMapping.isProperlyPaired());
}
@Test
public void testMapped()
{
String moreSam = "id 67 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertTrue(simpleMapping.isMapped());
assertTrue(simpleMapping.isMateMapped());
assertFalse(simpleMapping.isUnmapped());
assertFalse(simpleMapping.isMateUnmapped());
moreSam = "id 79 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertFalse(simpleMapping.isMapped());
assertFalse(simpleMapping.isMateMapped());
assertTrue(simpleMapping.isUnmapped());
assertTrue(simpleMapping.isMateUnmapped());
}
@Test
public void testStrand()
{
String moreSam = "id 67 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertFalse(simpleMapping.isOnReverse());
assertFalse(simpleMapping.isMateOnReverse());
moreSam = "id 115 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertTrue(simpleMapping.isOnReverse());
assertTrue(simpleMapping.isMateOnReverse());
}
@Test
public void testReadNumber()
{
String moreSam = "id 67 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertTrue(simpleMapping.isRead1());
assertFalse(simpleMapping.isRead2());
moreSam = "id 131 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertFalse(simpleMapping.isRead1());
assertTrue(simpleMapping.isRead2());
}
@Test
public void testSecondaryAlignment()
{
String moreSam = "id 67 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertFalse(simpleMapping.isSecondaryAlign());
moreSam = "id 387 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertTrue(simpleMapping.isSecondaryAlign());
}
@Test
public void testQcFlag()
{
String moreSam = "id 67 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertFalse(simpleMapping.isFailedQC());
moreSam = "id 512 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertTrue(simpleMapping.isFailedQC());
}
@Test
public void testDuplicateFlag()
{
String moreSam = "id 67 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertFalse(simpleMapping.isDuplicate());
moreSam = "id 1024 chr11 1 60 3M1I5M1D = 31 40 AGGAGAGGAG 1234512345";
simpleMapping = new SimpleSamMapping(moreSam);
assertTrue(simpleMapping.isDuplicate());
}
public static void main(String args[]) {
org.junit.runner.JUnitCore.main(TestAbstractSamMapping.class.getName());
}
}