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