JAL-147 prevent overview box move up over hidden first sequence
[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 sequence only
212      */
213     hs.showAll(null);
214     hs.hideSequence(seqs[0]);
215     for (int i = 1; i < height; i++)
216     {
217       assertEquals(i - 1, hs.findIndexWithoutHiddenSeqs(i));
218     }
219     assertEquals(-1, hs.findIndexWithoutHiddenSeqs(0));
220   }
221
222   /**
223    * Test the method that finds the visible row position a given distance before
224    * another row
225    */
226   @Test(groups = { "Functional" })
227   public void testFindIndexNFromRow()
228   {
229     AlignmentI al = new Alignment(seqs);
230     HiddenSequences hs = new HiddenSequences(al);
231
232     // test that without hidden rows, findIndexNFromRow returns
233     // position n above provided position
234     int pos = hs.subtractVisibleRows(3, 10);
235     assertEquals(7, pos);
236
237     // 0 returns same position
238     pos = hs.subtractVisibleRows(0, 10);
239     assertEquals(10, pos);
240
241     // overflow to top returns negative number
242     pos = hs.subtractVisibleRows(3, 0);
243     assertEquals(-3, pos);
244
245     // test that with hidden rows above result row
246     // behaviour is the same as above
247     hs.hideSequence(seqs[1]);
248     hs.hideSequence(seqs[2]);
249     hs.hideSequence(seqs[3]);
250
251     // position n above provided position
252     pos = hs.subtractVisibleRows(3, 10);
253     assertEquals(7, pos);
254
255     // 0 returns same position
256     pos = hs.subtractVisibleRows(0, 10);
257     assertEquals(10, pos);
258
259     // test with one set of hidden rows between start and required position
260     hs.hideSequence(seqs[12]);
261     hs.hideSequence(seqs[13]);
262     hs.hideSequence(seqs[14]);
263     hs.hideSequence(seqs[15]);
264     pos = hs.subtractVisibleRows(8, 17);
265     assertEquals(5, pos);
266
267     // test with two sets of hidden rows between start and required position
268     hs.hideSequence(seqs[20]);
269     hs.hideSequence(seqs[21]);
270     pos = hs.subtractVisibleRows(8, 23);
271     assertEquals(9, pos);
272
273     // repeat last 2 tests with no hidden columns to left of required position
274     hs.showAll(null);
275
276     // test with one set of hidden rows between start and required position
277     hs.hideSequence(seqs[12]);
278     hs.hideSequence(seqs[13]);
279     hs.hideSequence(seqs[14]);
280     hs.hideSequence(seqs[15]);
281     pos = hs.subtractVisibleRows(8, 17);
282     assertEquals(5, pos);
283
284     // test with two sets of hidden rows between start and required position
285     hs.hideSequence(seqs[20]);
286     hs.hideSequence(seqs[21]);
287     pos = hs.subtractVisibleRows(8, 23);
288     assertEquals(9, pos);
289
290   }
291
292   /**
293    * Test the method that reconstructs (sort of) the full alignment including
294    * hidden sequences
295    */
296   @Test(groups = "Functional")
297   public void testGetFullAlignment()
298   {
299     AlignmentI al = new Alignment(seqs);
300     assertArrayEquals(seqs, al.getSequencesArray());
301     al.setProperty("a", "b");
302     al.addAnnotation(new AlignmentAnnotation("ann", "label", 12f));
303     al.setSeqrep(seqs[4]);
304     SequenceGroup sg = new SequenceGroup();
305     sg.addSequence(seqs[8], false);
306     al.addGroup(sg);
307     ((Alignment) al).hasRNAStructure = true;
308
309     HiddenSequences hs = al.getHiddenSequences();
310     AlignmentI al2 = hs.getFullAlignment();
311     // new alignment but with original sequences
312     assertNotSame(al, al2);
313     assertArrayEquals(al.getSequencesArray(), al2.getSequencesArray());
314
315     hs.hideSequence(seqs[4]);
316     hs.hideSequence(seqs[9]);
317     al2 = hs.getFullAlignment();
318     assertNotSame(al, al2);
319     assertArrayEquals(seqs, al2.getSequencesArray());
320     assertNotNull(al2.getProperties());
321     assertSame(al.getProperties(), al2.getProperties());
322     assertNotNull(al2.getAlignmentAnnotation());
323     assertSame(al.getAlignmentAnnotation(), al2.getAlignmentAnnotation());
324     assertSame(seqs[4], al2.getSeqrep());
325     assertNotNull(al2.getGroups());
326     assertSame(al.getGroups(), al2.getGroups());
327     assertTrue(al2.hasRNAStructure());
328   }
329
330   /**
331    * Test the method that returns the hidden sequence at a given index in the
332    * full alignment
333    * 
334    * @return either the sequence (if hidden) or null (if not hidden)
335    */
336   @Test(groups = "Functional")
337   public void testGetHiddenSequence()
338   {
339     AlignmentI al = new Alignment(seqs);
340     HiddenSequences hs = al.getHiddenSequences();
341     assertNull(hs.getHiddenSequence(0));
342     hs.hideSequence(seqs[3]);
343     assertSame(seqs[3], hs.getHiddenSequence(3));
344     assertNull(hs.getHiddenSequence(2));
345     assertNull(hs.getHiddenSequence(4));
346   }
347
348   @Test(groups = "Functional")
349   public void testGetSize()
350   {
351   }
352
353   @Test(groups = "Functional")
354   public void testGetWidth()
355   {
356     AlignmentI al = new Alignment(seqs);
357     HiddenSequences hs = al.getHiddenSequences();
358     assertEquals(0, hs.getWidth());
359     hs.hideSequence(seqs[6]);
360     hs.hideSequence(seqs[8]);
361     assertEquals(9, hs.getWidth());
362   }
363
364   /**
365    * Test the method that adds a sequence to the hidden sequences and deletes it
366    * from the alignment, and its converse
367    */
368   @Test(groups = "Functional")
369   public void testHideShowSequence()
370   {
371     AlignmentI al = new Alignment(seqs);
372     assertTrue(al.getSequences().contains(seqs[1]));
373     HiddenSequences hs = al.getHiddenSequences();
374     assertEquals(0, hs.getSize());
375     assertEquals(SEQ_COUNT, al.getHeight());
376
377     /*
378      * hide the second sequence in the alignment
379      */
380     hs.hideSequence(seqs[1]);
381     assertFalse(hs.isHidden(seqs[0]));
382     assertTrue(hs.isHidden(seqs[1]));
383     assertFalse(al.getSequences().contains(seqs[1]));
384     assertEquals(1, hs.getSize());
385     assertEquals(SEQ_COUNT - 1, al.getHeight());
386     assertSame(seqs[2], al.getSequenceAt(1));
387
388     /*
389      * hide what is now the second sequence in the alignment
390      */
391     hs.hideSequence(seqs[2]);
392     assertFalse(hs.isHidden(seqs[0]));
393     assertTrue(hs.isHidden(seqs[1]));
394     assertTrue(hs.isHidden(seqs[2]));
395     assertFalse(al.getSequences().contains(seqs[1]));
396     assertFalse(al.getSequences().contains(seqs[2]));
397     assertEquals(2, hs.getSize());
398     assertEquals(SEQ_COUNT - 2, al.getHeight());
399
400     /*
401      * perform 'reveal' on what is now the second sequence in the alignment
402      * this should unhide the two sequences that precede it
403      */
404     List<SequenceI> revealed = hs.showSequence(1, null);
405     assertEquals(2, revealed.size());
406     assertTrue(revealed.contains(seqs[1]));
407     assertTrue(revealed.contains(seqs[2]));
408     assertEquals(0, hs.getSize());
409     assertEquals(SEQ_COUNT, al.getHeight());
410   }
411
412   /**
413    * Test the method that adds a sequence to the hidden sequences and deletes it
414    * from the alignment, and its converse, where the first hidden sequences are
415    * at the bottom of the alignment (JAL-2437)
416    */
417   @Test(groups = "Functional")
418   public void testHideShowLastSequences()
419   {
420     AlignmentI al = new Alignment(seqs);
421     assertTrue(al.getSequences().contains(seqs[1]));
422     HiddenSequences hs = al.getHiddenSequences();
423     assertEquals(0, hs.getSize());
424     assertEquals(SEQ_COUNT, al.getHeight());
425
426     /*
427      * hide the last sequence in the alignment
428      */
429     hs.hideSequence(seqs[SEQ_COUNT - 1]);
430     assertFalse(hs.isHidden(seqs[SEQ_COUNT - 2]));
431     assertTrue(hs.isHidden(seqs[SEQ_COUNT - 1]));
432     assertFalse(al.getSequences().contains(seqs[SEQ_COUNT - 1]));
433     assertEquals(1, hs.getSize());
434     assertEquals(SEQ_COUNT - 1, al.getHeight());
435
436     /*
437      * hide the third last sequence in the alignment
438      */
439     hs.hideSequence(seqs[SEQ_COUNT - 3]);
440     assertFalse(hs.isHidden(seqs[SEQ_COUNT - 2]));
441     assertTrue(hs.isHidden(seqs[SEQ_COUNT - 3]));
442     assertFalse(al.getSequences().contains(seqs[SEQ_COUNT - 3]));
443     assertEquals(2, hs.getSize());
444     assertEquals(SEQ_COUNT - 2, al.getHeight());
445
446     /*
447      * reveal all the sequences, which should be reinstated in the same order as they started in
448      */
449     hs.showAll(null);
450     assertFalse(hs.isHidden(seqs[SEQ_COUNT - 3]));
451     assertFalse(hs.isHidden(seqs[SEQ_COUNT - 1]));
452     assertEquals(seqs[SEQ_COUNT - 3], al.getSequences().get(SEQ_COUNT - 3));
453     assertEquals(seqs[SEQ_COUNT - 2], al.getSequences().get(SEQ_COUNT - 2));
454     assertEquals(seqs[SEQ_COUNT - 1], al.getSequences().get(SEQ_COUNT - 1));
455     assertEquals(0, hs.getSize());
456     assertEquals(SEQ_COUNT, al.getHeight());
457   }
458
459   @Test(groups = "Functional")
460   public void testIsHidden()
461   {
462     AlignmentI al = new Alignment(seqs);
463     HiddenSequences hs = al.getHiddenSequences();
464     hs.hideSequence(seqs[7]);
465     hs.hideSequence(seqs[4]);
466     assertTrue(hs.isHidden(seqs[4]));
467     assertFalse(hs.isHidden(seqs[5]));
468     assertFalse(hs.isHidden(seqs[6]));
469     assertTrue(hs.isHidden(seqs[7]));
470     assertFalse(hs.isHidden(null));
471     assertFalse(hs.isHidden(new Sequence("", "")));
472   }
473
474   /**
475    * Test hiding and unhiding a group with a representative sequence. The
476    * representative should be left visible when the group is hidden, and
477    * included in the selected group when it is unhidden.
478    */
479   @Test(groups = "Functional")
480   public void testHideShowSequence_withHiddenRepSequence()
481   {
482     AlignmentI al = new Alignment(seqs);
483
484     /*
485      * represent seqs 2-4 with seq3
486      * this hides seq2 and seq4 but not seq3
487      */
488     AlignViewport av = new AlignViewport(al);
489     SequenceGroup sg = new SequenceGroup();
490     sg.addSequence(seqs[1], false);
491     sg.addSequence(seqs[2], false);
492     sg.addSequence(seqs[3], false);
493     av.setSelectionGroup(sg);
494
495     /*
496      * hiding group with reference sequence is done via AlignViewport
497      */
498     av.hideSequences(seqs[2], true);
499     HiddenSequences hs = al.getHiddenSequences();
500     assertEquals(2, hs.getSize());
501     assertTrue(hs.isHidden(seqs[1]));
502     assertFalse(hs.isHidden(seqs[2]));
503     assertTrue(hs.isHidden(seqs[3]));
504
505     /*
506      * should now be no sequences selected in the alignment
507      */
508     assertNull(av.getSelectionGroup());
509
510     /*
511      * visible alignment is now seq0/2/4/5/6/7/8/9
512      * 'reveal sequences' at the representative sequence (index = 1)
513      * this should unhide the one above i.e. seq1
514      * and return a selection list including seq2
515      * 
516      * note have to call via AlignViewport to get the expected
517      * resulting sequence selection
518      */
519     av.showSequence(1);
520
521     /*
522      * only seq3 is now hidden
523      */
524     assertEquals(1, hs.getSize());
525     assertTrue(hs.isHidden(seqs[3]));
526     assertEquals(SEQ_COUNT - 1, al.getHeight());
527     sg = av.getSelectionGroup();
528
529     /*
530      * unhidden and representative sequence selected
531      * (this behaviour may change! JAL-2133)
532      */
533     assertEquals(2, sg.getSize());
534     assertTrue(sg.getSequences().contains(seqs[1]));
535     assertTrue(sg.getSequences().contains(seqs[2]));
536     assertFalse(sg.getSequences().contains(seqs[3]));
537   }
538 }