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