JAL-147 additions to unit test, check for negative array index
[jalview.git] / test / jalview / datamodel / HiddenSequencesTest.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.datamodel;
22
23 import static org.testng.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertFalse;
25 import static org.testng.AssertJUnit.assertNotNull;
26 import static org.testng.AssertJUnit.assertNotSame;
27 import static org.testng.AssertJUnit.assertNull;
28 import static org.testng.AssertJUnit.assertSame;
29 import static org.testng.AssertJUnit.assertTrue;
30 import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
31
32 import jalview.gui.AlignViewport;
33 import jalview.gui.JvOptionPane;
34
35 import java.util.List;
36
37 import org.testng.annotations.BeforeClass;
38 import org.testng.annotations.BeforeTest;
39 import org.testng.annotations.Test;
40
41 @Test(singleThreaded = true)
42 public class HiddenSequencesTest
43 {
44
45   @BeforeClass(alwaysRun = true)
46   public void setUpJvOptionPane()
47   {
48     JvOptionPane.setInteractiveMode(false);
49     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
50   }
51
52   static int SEQ_COUNT = 25;
53
54   SequenceI[] seqs;
55
56   /**
57    * Set up an alignment of 10 sequences
58    */
59   @BeforeTest(alwaysRun = true)
60   public void setUp()
61   {
62     seqs = new SequenceI[SEQ_COUNT];
63     for (int i = 0; i < SEQ_COUNT; i++)
64     {
65       // sequence lengths are 1, 2, ... 25
66       seqs[i] = new Sequence("Seq" + i,
67               "abcdefghijklmnopqrstuvwxy".substring(0, i + 1));
68     }
69   }
70
71   /**
72    * Test the method that converts sequence alignment index to what it would be
73    * if all sequences were unhidden
74    */
75   @Test(groups = "Functional")
76   public void testAdjustForHiddenSeqs()
77   {
78     AlignmentI al = new Alignment(seqs);
79     HiddenSequences hs = al.getHiddenSequences();
80     for (int i = 0; i < SEQ_COUNT; i++)
81     {
82       assertEquals(i, hs.adjustForHiddenSeqs(i));
83     }
84
85     // hide seq1 and seq5 and seq6
86     hs.hideSequence(seqs[1]);
87     hs.hideSequence(seqs[5]);
88     hs.hideSequence(seqs[6]);
89
90     /*
91      * alignment is now seq0/2/3/4/7/8/9
92      */
93     assertEquals(SEQ_COUNT - 3, al.getHeight());
94     assertEquals(0, hs.adjustForHiddenSeqs(0));
95     assertEquals(2, hs.adjustForHiddenSeqs(1));
96     assertEquals(3, hs.adjustForHiddenSeqs(2));
97     assertEquals(4, hs.adjustForHiddenSeqs(3));
98     assertEquals(7, hs.adjustForHiddenSeqs(4));
99     assertEquals(8, hs.adjustForHiddenSeqs(5));
100     assertEquals(9, hs.adjustForHiddenSeqs(6));
101   }
102
103   /**
104    * Test the method that increments the internal array size if a sequence is
105    * added to the alignment (ugh this should not be exposed to the light of day)
106    */
107   @Test(groups = "Functional")
108   public void testAdjustHeightSequenceAdded()
109   {
110     AlignmentI al = new Alignment(seqs);
111     assertEquals(SEQ_COUNT, al.getHeight());
112
113     HiddenSequences hs = al.getHiddenSequences();
114     // initially does nothing
115     hs.adjustHeightSequenceAdded();
116     assertNull(hs.hiddenSequences);
117
118     // hide one sequence
119     hs.hideSequence(seqs[3]);
120     assertEquals(1, hs.getSize());
121     assertEquals(SEQ_COUNT - 1, al.getHeight());
122     assertEquals(SEQ_COUNT, hs.hiddenSequences.length);
123
124     /*
125      * add a sequence to the alignment
126      * - the safe way to call hs.adjustHeightSequenceAdded!
127      * (implementation depends on alignment height having
128      * been already updated for the added sequence)
129      */
130     al.addSequence(new Sequence("a", "b"));
131     assertEquals(1, hs.getSize());
132     assertEquals(SEQ_COUNT, al.getHeight());
133     assertEquals(SEQ_COUNT + 1, hs.hiddenSequences.length);
134   }
135
136   /**
137    * Test the method that decrements the internal array size if a sequence is
138    * deleted from the alignment (ugh this should not be exposed to the light of
139    * day)
140    */
141   @Test(groups = "Functional")
142   public void testAdjustHeightSequenceDeleted()
143   {
144     AlignmentI al = new Alignment(seqs);
145     assertEquals(SEQ_COUNT, al.getHeight());
146
147     HiddenSequences hs = al.getHiddenSequences();
148     // initially does nothing
149     hs.adjustHeightSequenceAdded();
150     assertNull(hs.hiddenSequences);
151
152     // hide two sequences
153     hs.hideSequence(seqs[3]);
154     hs.hideSequence(seqs[5]);
155     assertEquals(2, hs.getSize());
156     assertTrue(hs.isHidden(seqs[3]));
157     assertTrue(hs.isHidden(seqs[5]));
158     assertEquals(SEQ_COUNT - 2, al.getHeight());
159     assertEquals(SEQ_COUNT, hs.hiddenSequences.length);
160
161     /*
162      * delete a visible sequence from the alignment
163      * - the safe way to call hs.adjustHeightSequenceDeleted!
164      * (implementation depends on alignment height having
165      * been already updated for the removed sequence)
166      */
167     al.deleteSequence(seqs[2]);
168     assertEquals(2, hs.getSize());
169     // the visible alignment is unchanged:
170     assertEquals(SEQ_COUNT - 3, al.getHeight());
171     // sequences array size has decremented:
172     assertEquals(SEQ_COUNT - 1, hs.hiddenSequences.length);
173   }
174
175   /**
176    * Test the method that converts a 'full alignment' sequence index into the
177    * equivalent in the alignment with sequences hidden
178    */
179   @Test(groups = "Functional")
180   public void testFindIndexWithoutHiddenSeqs()
181   {
182     AlignmentI al = new Alignment(seqs);
183     HiddenSequences hs = al.getHiddenSequences();
184     int height = al.getHeight();
185     for (int i = 0; i < height; i++)
186     {
187       assertEquals(i, hs.findIndexWithoutHiddenSeqs(i));
188     }
189
190     // hide seq1 and seq5 and seq6
191     hs.hideSequence(seqs[1]);
192     hs.hideSequence(seqs[5]);
193     hs.hideSequence(seqs[6]);
194
195     /*
196      * alignment is now seq0/2/3/4/7/8/9
197      */
198     assertEquals(height - 3, al.getHeight());
199     assertEquals(0, hs.findIndexWithoutHiddenSeqs(0));
200     assertEquals(0, hs.findIndexWithoutHiddenSeqs(1));
201     assertEquals(1, hs.findIndexWithoutHiddenSeqs(2));
202     assertEquals(2, hs.findIndexWithoutHiddenSeqs(3));
203     assertEquals(3, hs.findIndexWithoutHiddenSeqs(4));
204     assertEquals(3, hs.findIndexWithoutHiddenSeqs(5));
205     assertEquals(3, hs.findIndexWithoutHiddenSeqs(6));
206     assertEquals(4, hs.findIndexWithoutHiddenSeqs(7));
207     assertEquals(5, hs.findIndexWithoutHiddenSeqs(8));
208     assertEquals(6, hs.findIndexWithoutHiddenSeqs(9));
209
210     /*
211      * hide first two sequences
212      */
213     hs.showAll(null);
214     hs.hideSequence(seqs[0]);
215     hs.hideSequence(seqs[1]);
216     assertEquals(-1, hs.findIndexWithoutHiddenSeqs(0));
217     assertEquals(-1, hs.findIndexWithoutHiddenSeqs(1));
218     for (int i = 2; i < height; i++)
219     {
220       assertEquals(i - 2, hs.findIndexWithoutHiddenSeqs(i));
221     }
222   }
223
224   /**
225    * Test the method that finds the visible row position a given distance before
226    * another row
227    */
228   @Test(groups = { "Functional" })
229   public void testFindIndexNFromRow()
230   {
231     AlignmentI al = new Alignment(seqs);
232     HiddenSequences hs = new HiddenSequences(al);
233
234     // test that without hidden rows, findIndexNFromRow returns
235     // position n above provided position
236     int pos = hs.subtractVisibleRows(3, 10);
237     assertEquals(7, pos);
238
239     // 0 returns same position
240     pos = hs.subtractVisibleRows(0, 10);
241     assertEquals(10, pos);
242
243     // overflow to top returns negative number
244     pos = hs.subtractVisibleRows(3, 0);
245     assertEquals(-3, pos);
246
247     // test that with hidden rows above result row
248     // behaviour is the same as above
249     hs.hideSequence(seqs[1]);
250     hs.hideSequence(seqs[2]);
251     hs.hideSequence(seqs[3]);
252
253     // position n above provided position
254     pos = hs.subtractVisibleRows(3, 10);
255     assertEquals(7, pos);
256
257     // 0 returns same position
258     pos = hs.subtractVisibleRows(0, 10);
259     assertEquals(10, pos);
260
261     // test with one set of hidden rows between start and required position
262     hs.hideSequence(seqs[12]);
263     hs.hideSequence(seqs[13]);
264     hs.hideSequence(seqs[14]);
265     hs.hideSequence(seqs[15]);
266     pos = hs.subtractVisibleRows(8, 17);
267     assertEquals(5, pos);
268
269     // test with two sets of hidden rows between start and required position
270     hs.hideSequence(seqs[20]);
271     hs.hideSequence(seqs[21]);
272     pos = hs.subtractVisibleRows(8, 23);
273     assertEquals(9, pos);
274
275     // repeat last 2 tests with no hidden columns to left of required position
276     hs.showAll(null);
277
278     // test with one set of hidden rows between start and required position
279     hs.hideSequence(seqs[12]);
280     hs.hideSequence(seqs[13]);
281     hs.hideSequence(seqs[14]);
282     hs.hideSequence(seqs[15]);
283     pos = hs.subtractVisibleRows(8, 17);
284     assertEquals(5, pos);
285
286     // test with two sets of hidden rows between start and required position
287     hs.hideSequence(seqs[20]);
288     hs.hideSequence(seqs[21]);
289     pos = hs.subtractVisibleRows(8, 23);
290     assertEquals(9, pos);
291
292   }
293
294   /**
295    * Test the method that reconstructs (sort of) the full alignment including
296    * hidden sequences
297    */
298   @Test(groups = "Functional")
299   public void testGetFullAlignment()
300   {
301     AlignmentI al = new Alignment(seqs);
302     assertArrayEquals(seqs, al.getSequencesArray());
303     al.setProperty("a", "b");
304     al.addAnnotation(new AlignmentAnnotation("ann", "label", 12f));
305     al.setSeqrep(seqs[4]);
306     SequenceGroup sg = new SequenceGroup();
307     sg.addSequence(seqs[8], false);
308     al.addGroup(sg);
309     ((Alignment) al).hasRNAStructure = true;
310
311     HiddenSequences hs = al.getHiddenSequences();
312     AlignmentI al2 = hs.getFullAlignment();
313     // new alignment but with original sequences
314     assertNotSame(al, al2);
315     assertArrayEquals(al.getSequencesArray(), al2.getSequencesArray());
316
317     hs.hideSequence(seqs[4]);
318     hs.hideSequence(seqs[9]);
319     al2 = hs.getFullAlignment();
320     assertNotSame(al, al2);
321     assertArrayEquals(seqs, al2.getSequencesArray());
322     assertNotNull(al2.getProperties());
323     assertSame(al.getProperties(), al2.getProperties());
324     assertNotNull(al2.getAlignmentAnnotation());
325     assertSame(al.getAlignmentAnnotation(), al2.getAlignmentAnnotation());
326     assertSame(seqs[4], al2.getSeqrep());
327     assertNotNull(al2.getGroups());
328     assertSame(al.getGroups(), al2.getGroups());
329     assertTrue(al2.hasRNAStructure());
330   }
331
332   /**
333    * Test the method that returns the hidden sequence at a given index in the
334    * full alignment
335    * 
336    * @return either the sequence (if hidden) or null (if not hidden)
337    */
338   @Test(groups = "Functional")
339   public void testGetHiddenSequence()
340   {
341     AlignmentI al = new Alignment(seqs);
342     HiddenSequences hs = al.getHiddenSequences();
343     assertNull(hs.getHiddenSequence(0));
344     hs.hideSequence(seqs[3]);
345     assertSame(seqs[3], hs.getHiddenSequence(3));
346     assertNull(hs.getHiddenSequence(2));
347     assertNull(hs.getHiddenSequence(4));
348   }
349
350   @Test(groups = "Functional")
351   public void testGetSize()
352   {
353   }
354
355   @Test(groups = "Functional")
356   public void testGetWidth()
357   {
358     AlignmentI al = new Alignment(seqs);
359     HiddenSequences hs = al.getHiddenSequences();
360     assertEquals(0, hs.getWidth());
361     hs.hideSequence(seqs[6]);
362     hs.hideSequence(seqs[8]);
363     assertEquals(9, hs.getWidth());
364   }
365
366   /**
367    * Test the method that adds a sequence to the hidden sequences and deletes it
368    * from the alignment, and its converse
369    */
370   @Test(groups = "Functional")
371   public void testHideShowSequence()
372   {
373     AlignmentI al = new Alignment(seqs);
374     assertTrue(al.getSequences().contains(seqs[1]));
375     HiddenSequences hs = al.getHiddenSequences();
376     assertEquals(0, hs.getSize());
377     assertEquals(SEQ_COUNT, al.getHeight());
378
379     /*
380      * hide the second sequence in the alignment
381      */
382     hs.hideSequence(seqs[1]);
383     assertFalse(hs.isHidden(seqs[0]));
384     assertTrue(hs.isHidden(seqs[1]));
385     assertFalse(al.getSequences().contains(seqs[1]));
386     assertEquals(1, hs.getSize());
387     assertEquals(SEQ_COUNT - 1, al.getHeight());
388     assertSame(seqs[2], al.getSequenceAt(1));
389
390     /*
391      * hide what is now the second sequence in the alignment
392      */
393     hs.hideSequence(seqs[2]);
394     assertFalse(hs.isHidden(seqs[0]));
395     assertTrue(hs.isHidden(seqs[1]));
396     assertTrue(hs.isHidden(seqs[2]));
397     assertFalse(al.getSequences().contains(seqs[1]));
398     assertFalse(al.getSequences().contains(seqs[2]));
399     assertEquals(2, hs.getSize());
400     assertEquals(SEQ_COUNT - 2, al.getHeight());
401
402     /*
403      * perform 'reveal' on what is now the second sequence in the alignment
404      * this should unhide the two sequences that precede it
405      */
406     List<SequenceI> revealed = hs.showSequence(1, null);
407     assertEquals(2, revealed.size());
408     assertTrue(revealed.contains(seqs[1]));
409     assertTrue(revealed.contains(seqs[2]));
410     assertEquals(0, hs.getSize());
411     assertEquals(SEQ_COUNT, al.getHeight());
412   }
413
414   /**
415    * Test the method that adds a sequence to the hidden sequences and deletes it
416    * from the alignment, and its converse, where the first hidden sequences are
417    * at the bottom of the alignment (JAL-2437)
418    */
419   @Test(groups = "Functional")
420   public void testHideShowLastSequences()
421   {
422     AlignmentI al = new Alignment(seqs);
423     assertTrue(al.getSequences().contains(seqs[1]));
424     HiddenSequences hs = al.getHiddenSequences();
425     assertEquals(0, hs.getSize());
426     assertEquals(SEQ_COUNT, al.getHeight());
427
428     /*
429      * hide the last sequence in the alignment
430      */
431     hs.hideSequence(seqs[SEQ_COUNT - 1]);
432     assertFalse(hs.isHidden(seqs[SEQ_COUNT - 2]));
433     assertTrue(hs.isHidden(seqs[SEQ_COUNT - 1]));
434     assertFalse(al.getSequences().contains(seqs[SEQ_COUNT - 1]));
435     assertEquals(1, hs.getSize());
436     assertEquals(SEQ_COUNT - 1, al.getHeight());
437
438     /*
439      * hide the third last sequence in the alignment
440      */
441     hs.hideSequence(seqs[SEQ_COUNT - 3]);
442     assertFalse(hs.isHidden(seqs[SEQ_COUNT - 2]));
443     assertTrue(hs.isHidden(seqs[SEQ_COUNT - 3]));
444     assertFalse(al.getSequences().contains(seqs[SEQ_COUNT - 3]));
445     assertEquals(2, hs.getSize());
446     assertEquals(SEQ_COUNT - 2, al.getHeight());
447
448     /*
449      * reveal all the sequences, which should be reinstated in the same order as they started in
450      */
451     hs.showAll(null);
452     assertFalse(hs.isHidden(seqs[SEQ_COUNT - 3]));
453     assertFalse(hs.isHidden(seqs[SEQ_COUNT - 1]));
454     assertEquals(seqs[SEQ_COUNT - 3], al.getSequences().get(SEQ_COUNT - 3));
455     assertEquals(seqs[SEQ_COUNT - 2], al.getSequences().get(SEQ_COUNT - 2));
456     assertEquals(seqs[SEQ_COUNT - 1], al.getSequences().get(SEQ_COUNT - 1));
457     assertEquals(0, hs.getSize());
458     assertEquals(SEQ_COUNT, al.getHeight());
459   }
460
461   @Test(groups = "Functional")
462   public void testIsHidden()
463   {
464     AlignmentI al = new Alignment(seqs);
465     HiddenSequences hs = al.getHiddenSequences();
466     hs.hideSequence(seqs[7]);
467     hs.hideSequence(seqs[4]);
468     assertTrue(hs.isHidden(seqs[4]));
469     assertFalse(hs.isHidden(seqs[5]));
470     assertFalse(hs.isHidden(seqs[6]));
471     assertTrue(hs.isHidden(seqs[7]));
472     assertFalse(hs.isHidden(null));
473     assertFalse(hs.isHidden(new Sequence("", "")));
474   }
475
476   /**
477    * Test hiding and unhiding a group with a representative sequence. The
478    * representative should be left visible when the group is hidden, and
479    * included in the selected group when it is unhidden.
480    */
481   @Test(groups = "Functional")
482   public void testHideShowSequence_withHiddenRepSequence()
483   {
484     AlignmentI al = new Alignment(seqs);
485
486     /*
487      * represent seqs 2-4 with seq3
488      * this hides seq2 and seq4 but not seq3
489      */
490     AlignViewport av = new AlignViewport(al);
491     SequenceGroup sg = new SequenceGroup();
492     sg.addSequence(seqs[1], false);
493     sg.addSequence(seqs[2], false);
494     sg.addSequence(seqs[3], false);
495     av.setSelectionGroup(sg);
496
497     /*
498      * hiding group with reference sequence is done via AlignViewport
499      */
500     av.hideSequences(seqs[2], true);
501     HiddenSequences hs = al.getHiddenSequences();
502     assertEquals(2, hs.getSize());
503     assertTrue(hs.isHidden(seqs[1]));
504     assertFalse(hs.isHidden(seqs[2]));
505     assertTrue(hs.isHidden(seqs[3]));
506
507     /*
508      * should now be no sequences selected in the alignment
509      */
510     assertNull(av.getSelectionGroup());
511
512     /*
513      * visible alignment is now seq0/2/4/5/6/7/8/9
514      * 'reveal sequences' at the representative sequence (index = 1)
515      * this should unhide the one above i.e. seq1
516      * and return a selection list including seq2
517      * 
518      * note have to call via AlignViewport to get the expected
519      * resulting sequence selection
520      */
521     av.showSequence(1);
522
523     /*
524      * only seq3 is now hidden
525      */
526     assertEquals(1, hs.getSize());
527     assertTrue(hs.isHidden(seqs[3]));
528     assertEquals(SEQ_COUNT - 1, al.getHeight());
529     sg = av.getSelectionGroup();
530
531     /*
532      * unhidden and representative sequence selected
533      * (this behaviour may change! JAL-2133)
534      */
535     assertEquals(2, sg.getSize());
536     assertTrue(sg.getSequences().contains(seqs[1]));
537     assertTrue(sg.getSequences().contains(seqs[2]));
538     assertFalse(sg.getSequences().contains(seqs[3]));
539   }
540 }