JAL-1270 unit tests for HiddenSequences
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 27 Jun 2016 12:35:51 +0000 (13:35 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 27 Jun 2016 12:35:51 +0000 (13:35 +0100)
src/jalview/datamodel/HiddenSequences.java
test/jalview/datamodel/HiddenSequencesTest.java [new file with mode: 0644]

index db4c858..8ca3c5a 100755 (executable)
@@ -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<SequenceI> showSequence(int alignmentIndex,
           Map<SequenceI, SequenceCollectionI> 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 (file)
index 0000000..8b50a8b
--- /dev/null
@@ -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<SequenceI> 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]));
+  }
+}