/* * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * * Jalview 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. * * Jalview 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 Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.datamodel; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertSame; import static org.testng.AssertJUnit.assertTrue; import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals; import jalview.datamodel.PDBEntry.Type; import java.util.Arrays; import java.util.List; import java.util.Vector; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class SequenceTest { Sequence seq; @BeforeMethod(alwaysRun = true) public void setUp() { seq = new Sequence("FER1", "AKPNGVL"); } @Test(groups = { "Functional" }) public void testInsertGapsAndGapmaps() { SequenceI aseq = seq.deriveSequence(); aseq.insertCharAt(2, 3, '-'); aseq.insertCharAt(6, 3, '-'); assertEquals("Gap insertions not correct", "AK---P---NGVL", aseq.getSequenceAsString()); List gapInt = aseq.getInsertions(); assertEquals("Gap interval 1 start wrong", 2, gapInt.get(0)[0]); assertEquals("Gap interval 1 end wrong", 4, gapInt.get(0)[1]); assertEquals("Gap interval 2 start wrong", 6, gapInt.get(1)[0]); assertEquals("Gap interval 2 end wrong", 8, gapInt.get(1)[1]); } @Test(groups = { "Functional" }) public void testGetAnnotation() { // initial state returns null not an empty array assertNull(seq.getAnnotation()); AlignmentAnnotation ann = addAnnotation("label1", "desc1", "calcId1", 1f); AlignmentAnnotation[] anns = seq.getAnnotation(); assertEquals(1, anns.length); assertSame(ann, anns[0]); // removing all annotations reverts array to null seq.removeAlignmentAnnotation(ann); assertNull(seq.getAnnotation()); } @Test(groups = { "Functional" }) public void testGetAnnotation_forLabel() { AlignmentAnnotation ann1 = addAnnotation("label1", "desc1", "calcId1", 1f); AlignmentAnnotation ann2 = addAnnotation("label2", "desc2", "calcId2", 1f); AlignmentAnnotation ann3 = addAnnotation("label1", "desc3", "calcId3", 1f); AlignmentAnnotation[] anns = seq.getAnnotation("label1"); assertEquals(2, anns.length); assertSame(ann1, anns[0]); assertSame(ann3, anns[1]); } private AlignmentAnnotation addAnnotation(String label, String description, String calcId, float value) { final AlignmentAnnotation annotation = new AlignmentAnnotation(label, description, value); annotation.setCalcId(calcId); seq.addAlignmentAnnotation(annotation); return annotation; } @Test(groups = { "Functional" }) public void testGetAlignmentAnnotations_forCalcIdAndLabel() { AlignmentAnnotation ann1 = addAnnotation("label1", "desc1", "calcId1", 1f); AlignmentAnnotation ann2 = addAnnotation("label2", "desc2", "calcId2", 1f); AlignmentAnnotation ann3 = addAnnotation("label2", "desc3", "calcId3", 1f); AlignmentAnnotation ann4 = addAnnotation("label2", "desc3", "calcId2", 1f); AlignmentAnnotation ann5 = addAnnotation("label5", "desc3", null, 1f); AlignmentAnnotation ann6 = addAnnotation(null, "desc3", "calcId3", 1f); List anns = seq.getAlignmentAnnotations("calcId2", "label2"); assertEquals(2, anns.size()); assertSame(ann2, anns.get(0)); assertSame(ann4, anns.get(1)); assertTrue(seq.getAlignmentAnnotations("calcId2", "label3").isEmpty()); assertTrue(seq.getAlignmentAnnotations("calcId3", "label5").isEmpty()); assertTrue(seq.getAlignmentAnnotations("calcId2", null).isEmpty()); assertTrue(seq.getAlignmentAnnotations(null, "label3").isEmpty()); assertTrue(seq.getAlignmentAnnotations(null, null).isEmpty()); } /** * Tests for addAlignmentAnnotation. Note this method has the side-effect of * setting the sequenceRef on the annotation. Adding the same annotation twice * should be ignored. */ @Test(groups = { "Functional" }) public void testAddAlignmentAnnotation() { assertNull(seq.getAnnotation()); final AlignmentAnnotation annotation = new AlignmentAnnotation("a", "b", 2d); assertNull(annotation.sequenceRef); seq.addAlignmentAnnotation(annotation); assertSame(seq, annotation.sequenceRef); AlignmentAnnotation[] anns = seq.getAnnotation(); assertEquals(1, anns.length); assertSame(annotation, anns[0]); // re-adding does nothing seq.addAlignmentAnnotation(annotation); anns = seq.getAnnotation(); assertEquals(1, anns.length); assertSame(annotation, anns[0]); // an identical but different annotation can be added final AlignmentAnnotation annotation2 = new AlignmentAnnotation("a", "b", 2d); seq.addAlignmentAnnotation(annotation2); anns = seq.getAnnotation(); assertEquals(2, anns.length); assertSame(annotation, anns[0]); assertSame(annotation2, anns[1]); } @Test(groups = { "Functional" }) public void testGetStartGetEnd() { SequenceI sq = new Sequence("test", "ABCDEF"); assertEquals(1, sq.getStart()); assertEquals(6, sq.getEnd()); sq = new Sequence("test", "--AB-C-DEF--"); assertEquals(1, sq.getStart()); assertEquals(6, sq.getEnd()); sq = new Sequence("test", "----"); assertEquals(1, sq.getStart()); assertEquals(0, sq.getEnd()); // ?? } /** * Tests for the method that returns an alignment column position (base 1) for * a given sequence position (base 1). */ @Test(groups = { "Functional" }) public void testFindIndex() { SequenceI sq = new Sequence("test", "ABCDEF"); assertEquals(0, sq.findIndex(0)); assertEquals(1, sq.findIndex(1)); assertEquals(5, sq.findIndex(5)); assertEquals(6, sq.findIndex(6)); assertEquals(6, sq.findIndex(9)); sq = new Sequence("test", "-A--B-C-D-E-F--"); assertEquals(2, sq.findIndex(1)); assertEquals(5, sq.findIndex(2)); assertEquals(7, sq.findIndex(3)); // before start returns 0 assertEquals(0, sq.findIndex(0)); assertEquals(0, sq.findIndex(-1)); // beyond end returns last residue column assertEquals(13, sq.findIndex(99)); } /** * Tests for the method that returns a dataset sequence position (base 1) for * an aligned column position (base 0). */ @Test(groups = { "Functional" }) public void testFindPosition() { SequenceI sq = new Sequence("test", "ABCDEF"); assertEquals(1, sq.findPosition(0)); assertEquals(6, sq.findPosition(5)); // assertEquals(-1, seq.findPosition(6)); // fails sq = new Sequence("test", "AB-C-D--"); assertEquals(1, sq.findPosition(0)); assertEquals(2, sq.findPosition(1)); // gap position 'finds' residue to the right (not the left as per javadoc) assertEquals(3, sq.findPosition(2)); assertEquals(3, sq.findPosition(3)); assertEquals(4, sq.findPosition(4)); assertEquals(4, sq.findPosition(5)); // returns 1 more than sequence length if off the end ?!? assertEquals(5, sq.findPosition(6)); assertEquals(5, sq.findPosition(7)); sq = new Sequence("test", "--AB-C-DEF--"); assertEquals(1, sq.findPosition(0)); assertEquals(1, sq.findPosition(1)); assertEquals(1, sq.findPosition(2)); assertEquals(2, sq.findPosition(3)); assertEquals(3, sq.findPosition(4)); assertEquals(3, sq.findPosition(5)); assertEquals(4, sq.findPosition(6)); assertEquals(4, sq.findPosition(7)); assertEquals(5, sq.findPosition(8)); assertEquals(6, sq.findPosition(9)); assertEquals(7, sq.findPosition(10)); assertEquals(7, sq.findPosition(11)); } @Test(groups = { "Functional" }) public void testDeleteChars() { SequenceI sq = new Sequence("test", "ABCDEF"); assertEquals(1, sq.getStart()); assertEquals(6, sq.getEnd()); sq.deleteChars(2, 3); assertEquals("ABDEF", sq.getSequenceAsString()); assertEquals(1, sq.getStart()); assertEquals(5, sq.getEnd()); sq = new Sequence("test", "ABCDEF"); sq.deleteChars(0, 2); assertEquals("CDEF", sq.getSequenceAsString()); assertEquals(3, sq.getStart()); assertEquals(6, sq.getEnd()); } @Test(groups = { "Functional" }) public void testInsertCharAt() { // non-static methods: SequenceI sq = new Sequence("test", "ABCDEF"); sq.insertCharAt(0, 'z'); assertEquals("zABCDEF", sq.getSequenceAsString()); sq.insertCharAt(2, 2, 'x'); assertEquals("zAxxBCDEF", sq.getSequenceAsString()); // for static method see StringUtilsTest } /** * Test the method that returns an array of aligned sequence positions where * the array index is the data sequence position (both base 0). */ @Test(groups = { "Functional" }) public void testGapMap() { SequenceI sq = new Sequence("test", "-A--B-CD-E--F-"); sq.createDatasetSequence(); assertEquals("[1, 4, 6, 7, 9, 12]", Arrays.toString(sq.gapMap())); } /** * Test the method that gets sequence features, either from the sequence or * its dataset. */ @Test(groups = { "Functional" }) public void testGetSequenceFeatures() { SequenceI sq = new Sequence("test", "GATCAT"); sq.createDatasetSequence(); assertNull(sq.getSequenceFeatures()); /* * SequenceFeature on sequence */ SequenceFeature sf = new SequenceFeature(); sq.addSequenceFeature(sf); SequenceFeature[] sfs = sq.getSequenceFeatures(); assertEquals(1, sfs.length); assertSame(sf, sfs[0]); /* * SequenceFeature on sequence and dataset sequence; returns that on * sequence */ SequenceFeature sf2 = new SequenceFeature(); sq.getDatasetSequence().addSequenceFeature(sf2); sfs = sq.getSequenceFeatures(); assertEquals(1, sfs.length); assertSame(sf, sfs[0]); /* * SequenceFeature on dataset sequence only */ sq.setSequenceFeatures(null); sfs = sq.getSequenceFeatures(); assertEquals(1, sfs.length); assertSame(sf2, sfs[0]); /* * Corrupt case - no SequenceFeature, dataset's dataset is the original * sequence. Test shows no infinite loop results. */ sq.getDatasetSequence().setSequenceFeatures(null); sq.getDatasetSequence().setDatasetSequence(sq); // loop! assertNull(sq.getSequenceFeatures()); } /** * Test the method that returns an array, indexed by sequence position, whose * entries are the residue positions at the sequence position (or to the right * if a gap) */ @Test(groups = { "Functional" }) public void testFindPositionMap() { /* * Note: Javadoc for findPosition says it returns the residue position to * the left of a gapped position; in fact it returns the position to the * right. Also it returns a non-existent residue position for a gap beyond * the sequence. */ Sequence sq = new Sequence("TestSeq", "AB.C-D E."); int[] map = sq.findPositionMap(); assertEquals(Arrays.toString(new int[] { 1, 2, 3, 3, 4, 4, 5, 5, 6 }), Arrays.toString(map)); } /** * Test for getSubsequence */ @Test(groups = { "Functional" }) public void testGetSubsequence() { SequenceI sq = new Sequence("TestSeq", "ABCDEFG"); sq.createDatasetSequence(); // positions are base 0, end position is exclusive SequenceI subseq = sq.getSubSequence(2, 4); assertEquals("CD", subseq.getSequenceAsString()); // start/end are base 1 positions assertEquals(3, subseq.getStart()); assertEquals(4, subseq.getEnd()); // subsequence shares the full dataset sequence assertSame(sq.getDatasetSequence(), subseq.getDatasetSequence()); } /** * Test for deriveSequence applied to a sequence with a dataset */ @Test(groups = { "Functional" }) public void testDeriveSequence_existingDataset() { Sequence sq = new Sequence("Seq1", "CD"); sq.setDatasetSequence(new Sequence("Seq1", "ABCDEF")); sq.getDatasetSequence().addSequenceFeature( new SequenceFeature("", "", 1, 2, 0f, null)); sq.setStart(3); sq.setEnd(4); Sequence derived = (Sequence) sq.deriveSequence(); assertEquals("CD", derived.getSequenceAsString()); assertSame(sq.getDatasetSequence(), derived.getDatasetSequence()); assertNull(sq.sequenceFeatures); assertNull(derived.sequenceFeatures); // derived sequence should access dataset sequence features assertNotNull(sq.getSequenceFeatures()); assertArrayEquals(sq.getSequenceFeatures(), derived.getSequenceFeatures()); } /** * Test for deriveSequence applied to an ungapped sequence with no dataset */ @Test(groups = { "Functional" }) public void testDeriveSequence_noDatasetUngapped() { SequenceI sq = new Sequence("Seq1", "ABCDEF"); assertEquals(1, sq.getStart()); assertEquals(6, sq.getEnd()); SequenceI derived = sq.deriveSequence(); assertEquals("ABCDEF", derived.getSequenceAsString()); assertEquals("ABCDEF", derived.getDatasetSequence() .getSequenceAsString()); } /** * Test for deriveSequence applied to a gapped sequence with no dataset */ @Test(groups = { "Functional" }) public void testDeriveSequence_noDatasetGapped() { SequenceI sq = new Sequence("Seq1", "AB-C.D EF"); assertEquals(1, sq.getStart()); assertEquals(6, sq.getEnd()); assertNull(sq.getDatasetSequence()); SequenceI derived = sq.deriveSequence(); assertEquals("AB-C.D EF", derived.getSequenceAsString()); assertEquals("ABCDEF", derived.getDatasetSequence() .getSequenceAsString()); } @Test(groups = { "Functional" }) public void testCopyConstructor_noDataset() { SequenceI seq1 = new Sequence("Seq1", "AB-C.D EF"); seq1.setDescription("description"); seq1.addAlignmentAnnotation(new AlignmentAnnotation("label", "desc", 1.3d)); seq1.addSequenceFeature(new SequenceFeature("type", "desc", 22, 33, 12.4f, "group")); seq1.addPDBId(new PDBEntry("1A70", "B", Type.PDB, "File")); seq1.addDBRef(new DBRefEntry("EMBL", "1.2", "AZ12345")); SequenceI copy = new Sequence(seq1); assertNull(copy.getDatasetSequence()); verifyCopiedSequence(seq1, copy); // copy has a copy of the DBRefEntry // this is murky - DBrefs are only copied for dataset sequences // where the test for 'dataset sequence' is 'dataset is null' // but that doesn't distinguish it from an aligned sequence // which has not yet generated a dataset sequence // NB getDBRef looks inside dataset sequence if not null DBRefEntry[] dbrefs = copy.getDBRefs(); assertEquals(1, dbrefs.length); assertFalse(dbrefs[0] == seq1.getDBRefs()[0]); assertTrue(dbrefs[0].equals(seq1.getDBRefs()[0])); } @Test(groups = { "Functional" }) public void testCopyConstructor_withDataset() { SequenceI seq1 = new Sequence("Seq1", "AB-C.D EF"); seq1.createDatasetSequence(); seq1.setDescription("description"); seq1.addAlignmentAnnotation(new AlignmentAnnotation("label", "desc", 1.3d)); seq1.addSequenceFeature(new SequenceFeature("type", "desc", 22, 33, 12.4f, "group")); seq1.addPDBId(new PDBEntry("1A70", "B", Type.PDB, "File")); // here we add DBRef to the dataset sequence: seq1.getDatasetSequence().addDBRef( new DBRefEntry("EMBL", "1.2", "AZ12345")); SequenceI copy = new Sequence(seq1); assertNotNull(copy.getDatasetSequence()); assertSame(copy.getDatasetSequence(), seq1.getDatasetSequence()); verifyCopiedSequence(seq1, copy); // getDBRef looks inside dataset sequence and this is shared, // so holds the same dbref objects DBRefEntry[] dbrefs = copy.getDBRefs(); assertEquals(1, dbrefs.length); assertSame(dbrefs[0], seq1.getDBRefs()[0]); } /** * Helper to make assertions about a copied sequence * * @param seq1 * @param copy */ protected void verifyCopiedSequence(SequenceI seq1, SequenceI copy) { // verify basic properties: assertEquals(copy.getName(), seq1.getName()); assertEquals(copy.getDescription(), seq1.getDescription()); assertEquals(copy.getStart(), seq1.getStart()); assertEquals(copy.getEnd(), seq1.getEnd()); assertEquals(copy.getSequenceAsString(), seq1.getSequenceAsString()); // copy has a copy of the annotation: AlignmentAnnotation[] anns = copy.getAnnotation(); assertEquals(1, anns.length); assertFalse(anns[0] == seq1.getAnnotation()[0]); assertEquals(anns[0].label, seq1.getAnnotation()[0].label); assertEquals(anns[0].description, seq1.getAnnotation()[0].description); assertEquals(anns[0].score, seq1.getAnnotation()[0].score); // copy has a copy of the sequence feature: SequenceFeature[] sfs = copy.getSequenceFeatures(); assertEquals(1, sfs.length); if (seq1.getDatasetSequence()!=null && copy.getDatasetSequence()==seq1.getDatasetSequence()) { assertTrue(sfs[0] == seq1.getSequenceFeatures()[0]); } else { assertFalse(sfs[0] == seq1.getSequenceFeatures()[0]); } assertTrue(sfs[0].equals(seq1.getSequenceFeatures()[0])); // copy has a copy of the PDB entry Vector pdbs = copy.getAllPDBEntries(); assertEquals(1, pdbs.size()); assertFalse(pdbs.get(0) == seq1.getAllPDBEntries().get(0)); assertTrue(pdbs.get(0).equals(seq1.getAllPDBEntries().get(0))); } @Test(groups = "Functional") public void testGetCharAt() { SequenceI sq = new Sequence("", "abcde"); assertEquals('a', sq.getCharAt(0)); assertEquals('e', sq.getCharAt(4)); assertEquals(' ', sq.getCharAt(5)); assertEquals(' ', sq.getCharAt(-1)); } }