From a457753abc62fe63774215de98f74f5c6aeb10d4 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Mon, 27 Jun 2016 13:35:51 +0100 Subject: [PATCH] JAL-1270 unit tests for HiddenSequences --- src/jalview/datamodel/HiddenSequences.java | 96 ++++-- test/jalview/datamodel/HiddenSequencesTest.java | 378 +++++++++++++++++++++++ 2 files changed, 449 insertions(+), 25 deletions(-) create mode 100644 test/jalview/datamodel/HiddenSequencesTest.java diff --git a/src/jalview/datamodel/HiddenSequences.java b/src/jalview/datamodel/HiddenSequences.java index db4c858..8ca3c5a 100755 --- a/src/jalview/datamodel/HiddenSequences.java +++ b/src/jalview/datamodel/HiddenSequences.java @@ -33,11 +33,21 @@ public class HiddenSequences AlignmentI alignment; + /** + * Constructor given a reference to an alignment (with no hidden sequences) + * + * @param al + */ public HiddenSequences(AlignmentI al) { alignment = al; } + /** + * Answers the number of hidden sequences + * + * @return + */ public int getSize() { if (hiddenSequences == null) @@ -45,9 +55,9 @@ public class HiddenSequences return 0; } int count = 0; - for (int i = 0; i < hiddenSequences.length; i++) + for (SequenceI seq : hiddenSequences) { - if (hiddenSequences[i] != null) + if (seq != null) { count++; } @@ -56,15 +66,23 @@ public class HiddenSequences return count; } + /** + * Answers the length of the longest hidden sequence + * + * @return + */ public int getWidth() { + if (hiddenSequences == null) + { + return 0; + } int width = 0; - for (int i = 0; i < hiddenSequences.length; i++) + for (SequenceI seq : hiddenSequences) { - if (hiddenSequences[i] != null - && hiddenSequences[i].getLength() > width) + if (seq != null && seq.getLength() > width) { - width = hiddenSequences[i].getLength(); + width = seq.getLength(); } } @@ -72,7 +90,7 @@ public class HiddenSequences } /** - * Call this method if sequences are removed from the main alignment + * Call this method after a sequence is removed from the main alignment */ public void adjustHeightSequenceDeleted(int seqIndex) { @@ -108,8 +126,7 @@ public class HiddenSequences } /** - * Call this method if sequences are added to or removed from the main - * alignment + * Call this method after a sequence is added to the main alignment */ public void adjustHeightSequenceAdded() { @@ -125,6 +142,11 @@ public class HiddenSequences hiddenSequences = tmp; } + /** + * Mark the specified sequence as hidden + * + * @param sequence + */ public void hideSequence(SequenceI sequence) { if (hiddenSequences == null) @@ -163,6 +185,17 @@ public class HiddenSequences return revealedSeqs; } + /** + * Reveals (unhides) consecutive hidden sequences just above the given + * alignment index. The revealed sequences are selected (including their + * visible representative sequence if there was one and 'reveal' is being + * performed on it). + * + * @param alignmentIndex + * @param hiddenRepSequences + * a map of representative sequences to the sequences they represent + * @return + */ public List showSequence(int alignmentIndex, Map hiddenRepSequences) { @@ -203,20 +236,22 @@ public class HiddenSequences + " has been deleted whilst hidden"); } } - } } - return revealedSeqs; } public SequenceI getHiddenSequence(int alignmentIndex) { - return hiddenSequences[alignmentIndex]; + return hiddenSequences == null ? null : hiddenSequences[alignmentIndex]; } public int findIndexWithoutHiddenSeqs(int alignmentIndex) { + if (hiddenSequences == null) + { + return alignmentIndex; + } int index = 0; int hiddenSeqs = 0; if (hiddenSequences.length <= alignmentIndex) @@ -232,13 +267,16 @@ public class HiddenSequences } index++; } - ; return (alignmentIndex - hiddenSeqs); } public int adjustForHiddenSeqs(int alignmentIndex) { + if (hiddenSequences == null) + { + return alignmentIndex; + } int index = 0; int hSize = hiddenSequences.length; while (index <= alignmentIndex && index < hSize) @@ -262,20 +300,28 @@ public class HiddenSequences */ public AlignmentI getFullAlignment() { - int isize = hiddenSequences.length; - SequenceI[] seq = new Sequence[isize]; - - int index = 0; - for (int i = 0; i < hiddenSequences.length; i++) + SequenceI[] seq; + if (hiddenSequences == null) { - if (hiddenSequences[i] != null) - { - seq[i] = hiddenSequences[i]; - } - else + seq = alignment.getSequencesArray(); + } + else + { + int isize = hiddenSequences.length; + seq = new Sequence[isize]; + + int index = 0; + for (int i = 0; i < hiddenSequences.length; i++) { - seq[i] = alignment.getSequenceAt(index); - index++; + if (hiddenSequences[i] != null) + { + seq[i] = hiddenSequences[i]; + } + else + { + seq[i] = alignment.getSequenceAt(index); + index++; + } } } Alignment fAlignmt = new Alignment(seq); diff --git a/test/jalview/datamodel/HiddenSequencesTest.java b/test/jalview/datamodel/HiddenSequencesTest.java new file mode 100644 index 0000000..8b50a8b --- /dev/null +++ b/test/jalview/datamodel/HiddenSequencesTest.java @@ -0,0 +1,378 @@ +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.assertNotSame; +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.gui.AlignViewport; + +import java.util.List; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +@Test(singleThreaded = true) +public class HiddenSequencesTest +{ + static int SEQ_COUNT = 10; + + SequenceI[] seqs; + + /** + * Set up an alignment of 10 sequences + */ + @BeforeTest(alwaysRun = true) + public void setUp() + { + seqs = new SequenceI[SEQ_COUNT]; + for (int i = 0; i < SEQ_COUNT; i++) + { + // sequence lengths are 1, 2, ... 10 + seqs[i] = new Sequence("Seq" + i, "abcdefghijk".substring(0, i + 1)); + } + } + + /** + * Test the method that converts sequence alignment index to what it would be + * if all sequences were unhidden + */ + @Test(groups = "Functional") + public void testAdjustForHiddenSeqs() + { + AlignmentI al = new Alignment(seqs); + HiddenSequences hs = al.getHiddenSequences(); + for (int i = 0; i < SEQ_COUNT; i++) + { + assertEquals(i, hs.adjustForHiddenSeqs(i)); + } + + // hide seq1 and seq5 and seq6 + hs.hideSequence(seqs[1]); + hs.hideSequence(seqs[5]); + hs.hideSequence(seqs[6]); + + /* + * alignment is now seq0/2/3/4/7/8/9 + */ + assertEquals(7, al.getHeight()); + assertEquals(0, hs.adjustForHiddenSeqs(0)); + assertEquals(2, hs.adjustForHiddenSeqs(1)); + assertEquals(3, hs.adjustForHiddenSeqs(2)); + assertEquals(4, hs.adjustForHiddenSeqs(3)); + assertEquals(7, hs.adjustForHiddenSeqs(4)); + assertEquals(8, hs.adjustForHiddenSeqs(5)); + assertEquals(9, hs.adjustForHiddenSeqs(6)); + } + + /** + * Test the method that increments the internal array size if a sequence is + * added to the alignment (ugh this should not be exposed to the light of day) + */ + @Test(groups = "Functional") + public void testAdjustHeightSequenceAdded() + { + AlignmentI al = new Alignment(seqs); + assertEquals(SEQ_COUNT, al.getHeight()); + + HiddenSequences hs = al.getHiddenSequences(); + // initially does nothing + hs.adjustHeightSequenceAdded(); + assertNull(hs.hiddenSequences); + + // hide one sequence + hs.hideSequence(seqs[3]); + assertEquals(1, hs.getSize()); + assertEquals(SEQ_COUNT - 1, al.getHeight()); + assertEquals(SEQ_COUNT, hs.hiddenSequences.length); + + /* + * add a sequence to the alignment + * - the safe way to call hs.adjustHeightSequenceAdded! + * (implementation depends on alignment height having + * been already updated for the added sequence) + */ + al.addSequence(new Sequence("a", "b")); + assertEquals(1, hs.getSize()); + assertEquals(SEQ_COUNT, al.getHeight()); + assertEquals(SEQ_COUNT + 1, hs.hiddenSequences.length); + } + + /** + * Test the method that decrements the internal array size if a sequence is + * deleted from the alignment (ugh this should not be exposed to the light of + * day) + */ + @Test(groups = "Functional") + public void testAdjustHeightSequenceDeleted() + { + AlignmentI al = new Alignment(seqs); + assertEquals(SEQ_COUNT, al.getHeight()); + + HiddenSequences hs = al.getHiddenSequences(); + // initially does nothing + hs.adjustHeightSequenceAdded(); + assertNull(hs.hiddenSequences); + + // hide two sequences + hs.hideSequence(seqs[3]); + hs.hideSequence(seqs[5]); + assertEquals(2, hs.getSize()); + assertTrue(hs.isHidden(seqs[3])); + assertTrue(hs.isHidden(seqs[5])); + assertEquals(SEQ_COUNT - 2, al.getHeight()); + assertEquals(SEQ_COUNT, hs.hiddenSequences.length); + + /* + * delete a visible sequence from the alignment + * - the safe way to call hs.adjustHeightSequenceDeleted! + * (implementation depends on alignment height having + * been already updated for the removed sequence) + */ + al.deleteSequence(seqs[2]); + assertEquals(2, hs.getSize()); + // the visible alignment is unchanged: + assertEquals(SEQ_COUNT - 3, al.getHeight()); + // sequences array size has decremented: + assertEquals(SEQ_COUNT - 1, hs.hiddenSequences.length); + } + + /** + * Test the method that converts a 'full alignment' sequence index into the + * equivalent in the alignment with sequences hidden + */ + @Test(groups = "Functional") + public void testFindIndexWithoutHiddenSeqs() + { + AlignmentI al = new Alignment(seqs); + HiddenSequences hs = al.getHiddenSequences(); + for (int i = 0; i < SEQ_COUNT; i++) + { + assertEquals(i, hs.findIndexWithoutHiddenSeqs(i)); + } + + // hide seq1 and seq5 and seq6 + hs.hideSequence(seqs[1]); + hs.hideSequence(seqs[5]); + hs.hideSequence(seqs[6]); + + /* + * alignment is now seq0/2/3/4/7/8/9 + */ + assertEquals(7, al.getHeight()); + assertEquals(0, hs.findIndexWithoutHiddenSeqs(0)); + assertEquals(0, hs.findIndexWithoutHiddenSeqs(1)); + assertEquals(1, hs.findIndexWithoutHiddenSeqs(2)); + assertEquals(2, hs.findIndexWithoutHiddenSeqs(3)); + assertEquals(3, hs.findIndexWithoutHiddenSeqs(4)); + assertEquals(3, hs.findIndexWithoutHiddenSeqs(5)); + assertEquals(3, hs.findIndexWithoutHiddenSeqs(6)); + assertEquals(4, hs.findIndexWithoutHiddenSeqs(7)); + assertEquals(5, hs.findIndexWithoutHiddenSeqs(8)); + assertEquals(6, hs.findIndexWithoutHiddenSeqs(9)); + } + + /** + * Test the method that reconstructs (sort of) the full alignment including + * hidden sequences + */ + @Test(groups = "Functional") + public void testGetFullAlignment() + { + AlignmentI al = new Alignment(seqs); + assertArrayEquals(seqs, al.getSequencesArray()); + al.setProperty("a", "b"); + al.addAnnotation(new AlignmentAnnotation("ann", "label", 12f)); + al.setSeqrep(seqs[4]); + SequenceGroup sg = new SequenceGroup(); + sg.addSequence(seqs[8], false); + al.addGroup(sg); + ((Alignment) al).hasRNAStructure = true; + + HiddenSequences hs = al.getHiddenSequences(); + AlignmentI al2 = hs.getFullAlignment(); + // new alignment but with original sequences + assertNotSame(al, al2); + assertArrayEquals(al.getSequencesArray(), al2.getSequencesArray()); + + hs.hideSequence(seqs[4]); + hs.hideSequence(seqs[9]); + al2 = hs.getFullAlignment(); + assertNotSame(al, al2); + assertArrayEquals(seqs, al2.getSequencesArray()); + assertNotNull(al2.getProperties()); + assertSame(al.getProperties(), al2.getProperties()); + assertNotNull(al2.getAlignmentAnnotation()); + assertSame(al.getAlignmentAnnotation(), al2.getAlignmentAnnotation()); + assertSame(seqs[4], al2.getSeqrep()); + assertNotNull(al2.getGroups()); + assertSame(al.getGroups(), al2.getGroups()); + assertTrue(al2.hasRNAStructure()); + } + + /** + * Test the method that returns the hidden sequence at a given index in the + * full alignment + * + * @return either the sequence (if hidden) or null (if not hidden) + */ + @Test(groups = "Functional") + public void testGetHiddenSequence() + { + AlignmentI al = new Alignment(seqs); + HiddenSequences hs = al.getHiddenSequences(); + assertNull(hs.getHiddenSequence(0)); + hs.hideSequence(seqs[3]); + assertSame(seqs[3], hs.getHiddenSequence(3)); + assertNull(hs.getHiddenSequence(2)); + assertNull(hs.getHiddenSequence(4)); + } + + @Test(groups = "Functional") + public void testGetSize() + { + } + + @Test(groups = "Functional") + public void testGetWidth() + { + AlignmentI al = new Alignment(seqs); + HiddenSequences hs = al.getHiddenSequences(); + assertEquals(0, hs.getWidth()); + hs.hideSequence(seqs[6]); + hs.hideSequence(seqs[8]); + assertEquals(9, hs.getWidth()); + } + + /** + * Test the method that adds a sequence to the hidden sequences and deletes it + * from the alignment, and its converse + */ + @Test(groups = "Functional") + public void testHideShowSequence() + { + AlignmentI al = new Alignment(seqs); + assertTrue(al.getSequences().contains(seqs[1])); + HiddenSequences hs = al.getHiddenSequences(); + assertEquals(0, hs.getSize()); + assertEquals(10, al.getHeight()); + + /* + * hide the second sequence in the alignment + */ + hs.hideSequence(seqs[1]); + assertFalse(hs.isHidden(seqs[0])); + assertTrue(hs.isHidden(seqs[1])); + assertFalse(al.getSequences().contains(seqs[1])); + assertEquals(1, hs.getSize()); + assertEquals(9, al.getHeight()); + assertSame(seqs[2], al.getSequenceAt(1)); + + /* + * hide what is now the second sequence in the alignment + */ + hs.hideSequence(seqs[2]); + assertFalse(hs.isHidden(seqs[0])); + assertTrue(hs.isHidden(seqs[1])); + assertTrue(hs.isHidden(seqs[2])); + assertFalse(al.getSequences().contains(seqs[1])); + assertFalse(al.getSequences().contains(seqs[2])); + assertEquals(2, hs.getSize()); + assertEquals(8, al.getHeight()); + + /* + * perform 'reveal' on what is now the second sequence in the alignment + * this should unhide the two sequences that precede it + */ + List revealed = hs.showSequence(1, null); + assertEquals(2, revealed.size()); + assertTrue(revealed.contains(seqs[1])); + assertTrue(revealed.contains(seqs[2])); + assertEquals(0, hs.getSize()); + assertEquals(10, al.getHeight()); + } + + @Test(groups = "Functional") + public void testIsHidden() + { + AlignmentI al = new Alignment(seqs); + HiddenSequences hs = al.getHiddenSequences(); + hs.hideSequence(seqs[7]); + hs.hideSequence(seqs[4]); + assertTrue(hs.isHidden(seqs[4])); + assertFalse(hs.isHidden(seqs[5])); + assertFalse(hs.isHidden(seqs[6])); + assertTrue(hs.isHidden(seqs[7])); + assertFalse(hs.isHidden(null)); + assertFalse(hs.isHidden(new Sequence("", ""))); + } + + /** + * Test hiding and unhiding a group with a representative sequence. The + * representative should be left visible when the group is hidden, and + * included in the selected group when it is unhidden. + */ + @Test(groups = "Functional") + public void testHideShowSequence_withHiddenRepSequence() + { + AlignmentI al = new Alignment(seqs); + + /* + * represent seqs 2-4 with seq3 + * this hides seq2 and seq4 but not seq3 + */ + AlignViewport av = new AlignViewport(al); + SequenceGroup sg = new SequenceGroup(); + sg.addSequence(seqs[1], false); + sg.addSequence(seqs[2], false); + sg.addSequence(seqs[3], false); + av.setSelectionGroup(sg); + + /* + * hiding group with reference sequence is done via AlignViewport + */ + av.hideSequences(seqs[2], true); + HiddenSequences hs = al.getHiddenSequences(); + assertEquals(2, hs.getSize()); + assertTrue(hs.isHidden(seqs[1])); + assertFalse(hs.isHidden(seqs[2])); + assertTrue(hs.isHidden(seqs[3])); + + /* + * should now be no sequences selected in the alignment + */ + assertNull(av.getSelectionGroup()); + + /* + * visible alignment is now seq0/2/4/5/6/7/8/9 + * 'reveal sequences' at the representative sequence (index = 1) + * this should unhide the one above i.e. seq1 + * and return a selection list including seq2 + * + * note have to call via AlignViewport to get the expected + * resulting sequence selection + */ + av.showSequence(1); + + /* + * only seq3 is now hidden + */ + assertEquals(1, hs.getSize()); + assertTrue(hs.isHidden(seqs[3])); + assertEquals(SEQ_COUNT - 1, al.getHeight()); + sg = av.getSelectionGroup(); + + /* + * unhidden and representative sequence selected + * (this behaviour may change! JAL-2133) + */ + assertEquals(2, sg.getSize()); + assertTrue(sg.getSequences().contains(seqs[1])); + assertTrue(sg.getSequences().contains(seqs[2])); + assertFalse(sg.getSequences().contains(seqs[3])); + } +} -- 1.7.10.2