JAL-2172 unit test added
[jalview.git] / test / jalview / datamodel / ColumnSelectionTest.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.assertSame;
26 import static org.testng.AssertJUnit.assertTrue;
27
28 import java.util.Arrays;
29 import java.util.List;
30
31 import org.testng.annotations.Test;
32
33 public class ColumnSelectionTest
34 {
35
36   @Test(groups = { "Functional" })
37   public void testAddElement()
38   {
39     ColumnSelection cs = new ColumnSelection();
40     cs.addElement(2);
41     cs.addElement(5);
42     cs.addElement(3);
43     cs.addElement(5); // ignored
44     List<Integer> sel = cs.getSelected();
45     assertEquals("[2, 5, 3]", sel.toString());
46   }
47
48   /**
49    * Test the remove method - in particular to verify that remove(int i) removes
50    * the element whose value is i, _NOT_ the i'th element.
51    */
52   @Test(groups = { "Functional" })
53   public void testRemoveElement()
54   {
55     ColumnSelection cs = new ColumnSelection();
56     cs.addElement(2);
57     cs.addElement(5);
58
59     // removing elements not in the list has no effect
60     cs.removeElement(0);
61     cs.removeElement(1);
62     List<Integer> sel = cs.getSelected();
63     assertEquals(2, sel.size());
64     assertEquals(new Integer(2), sel.get(0));
65     assertEquals(new Integer(5), sel.get(1));
66
67     // removing an element in the list removes it
68     cs.removeElement(2);
69     assertEquals(1, sel.size());
70     assertEquals(new Integer(5), sel.get(0));
71   }
72
73   /**
74    * Test the method that finds the visible column position of an alignment
75    * column, allowing for hidden columns.
76    */
77   @Test(groups = { "Functional" })
78   public void testFindColumnPosition()
79   {
80     ColumnSelection cs = new ColumnSelection();
81     assertEquals(5, cs.findColumnPosition(5));
82
83     // hiding column 6 makes no difference
84     cs.hideColumns(6, 6);
85     assertEquals(5, cs.findColumnPosition(5));
86
87     // hiding column 4 moves column 5 to column 4
88     cs.hideColumns(4, 4);
89     assertEquals(4, cs.findColumnPosition(5));
90
91     // hiding columns 1 and 2 moves column 5 to column 2
92     cs.hideColumns(1, 2);
93     assertEquals(2, cs.findColumnPosition(5));
94   }
95
96   /**
97    * Test the code used to locate the reference sequence ruler origin
98    */
99   @Test(groups = { "Functional" })
100   public void testLocateVisibleBoundsofSequence()
101   {
102     ColumnSelection cs = new ColumnSelection();
103     SequenceI seq = new Sequence("RefSeq", "-A-SD-ASD--E---");
104     assertEquals(2, seq.findIndex(seq.getStart()));
105
106     // no hidden columns
107     assertEquals(
108             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
109                 seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
110                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
111                 seq.findIndex(seq.getEnd()) - 1 }),
112             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
113
114     // hidden column on gap after end of sequence - should not affect bounds
115     cs.hideColumns(13);
116     assertEquals(
117             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
118                 seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
119                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
120                 seq.findIndex(seq.getEnd()) - 1 }),
121             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
122
123     cs.revealAllHiddenColumns();
124     // hidden column on gap before beginning of sequence - should vis bounds by
125     // one
126     cs.hideColumns(0);
127     assertEquals(
128             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 2,
129                 seq.findIndex(seq.getEnd()) - 2, seq.getStart(),
130                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
131                 seq.findIndex(seq.getEnd()) - 1 }),
132             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
133
134     cs.revealAllHiddenColumns();
135     // hide columns around most of sequence - leave one residue remaining
136     cs.hideColumns(1, 3);
137     cs.hideColumns(6, 11);
138     assertEquals("-D",
139             cs.getVisibleSequenceStrings(0, 5, new SequenceI[] { seq })[0]);
140     assertEquals(
141             Arrays.toString(new int[] { 1, 1, 3, 3,
142                 seq.findIndex(seq.getStart()) - 1,
143                 seq.findIndex(seq.getEnd()) - 1 }),
144             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
145     cs.revealAllHiddenColumns();
146
147     // hide whole sequence - should just get location of hidden region
148     // containing sequence
149     cs.hideColumns(1, 11);
150     assertEquals(
151             Arrays.toString(new int[] { 0, 1, 0, 0,
152                 seq.findIndex(seq.getStart()) - 1,
153                 seq.findIndex(seq.getEnd()) - 1 }),
154             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
155
156   }
157
158   @Test(groups={"Functional"})
159   public void testLocateVisibleBoundsPathologicals()
160   {
161     // test some pathological cases we missed
162     AlignmentI al = new Alignment(new SequenceI[] { new Sequence("refseqGaptest","KTDVTI----------NFI-----G----L")});
163     ColumnSelection cs = new ColumnSelection();
164     cs.hideInsertionsFor(al.getSequenceAt(0));
165     assertEquals(
166             "G",
167             ""
168                     + al.getSequenceAt(0).getCharAt(
169                             cs.adjustForHiddenColumns(9)));
170
171
172   }
173   @Test(groups = { "Functional" })
174   public void testHideColumns()
175   {
176     ColumnSelection cs = new ColumnSelection();
177     cs.hideColumns(5);
178     List<int[]> hidden = cs.getHiddenColumns();
179     assertEquals(1, hidden.size());
180     assertEquals("[5, 5]", Arrays.toString(hidden.get(0)));
181
182     cs.hideColumns(3);
183     assertEquals(2, hidden.size());
184     // two hidden ranges, in order:
185     assertSame(hidden, cs.getHiddenColumns());
186     assertEquals("[3, 3]", Arrays.toString(hidden.get(0)));
187     assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
188
189     // hiding column 4 expands [3, 3] to [3, 4]
190     // not fancy enough to coalesce this into [3, 5] though
191     cs.hideColumns(4);
192     hidden = cs.getHiddenColumns();
193     assertEquals(2, hidden.size());
194     assertEquals("[3, 4]", Arrays.toString(hidden.get(0)));
195     assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
196
197     // clear hidden columns (note they are added to selected)
198     cs.revealAllHiddenColumns();
199     // it is now actually null but getter returns an empty list
200     assertTrue(cs.getHiddenColumns().isEmpty());
201
202     cs.hideColumns(3, 6);
203     hidden = cs.getHiddenColumns();
204     int[] firstHiddenRange = hidden.get(0);
205     assertEquals("[3, 6]", Arrays.toString(firstHiddenRange));
206
207     // adding a subrange of already hidden should do nothing
208     cs.hideColumns(4, 5);
209     assertEquals(1, hidden.size());
210     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
211     cs.hideColumns(3, 5);
212     assertEquals(1, hidden.size());
213     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
214     cs.hideColumns(4, 6);
215     assertEquals(1, hidden.size());
216     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
217     cs.hideColumns(3, 6);
218     assertEquals(1, hidden.size());
219     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
220
221     cs.revealAllHiddenColumns();
222     cs.hideColumns(2, 4);
223     hidden = cs.getHiddenColumns();
224     assertEquals(1, hidden.size());
225     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
226
227     // extend contiguous with 2 positions overlap
228     cs.hideColumns(3, 5);
229     assertEquals(1, hidden.size());
230     assertEquals("[2, 5]", Arrays.toString(hidden.get(0)));
231
232     // extend contiguous with 1 position overlap
233     cs.hideColumns(5, 6);
234     assertEquals(1, hidden.size());
235     assertEquals("[2, 6]", Arrays.toString(hidden.get(0)));
236
237     // extend contiguous with overlap both ends:
238     cs.hideColumns(1, 7);
239     assertEquals(1, hidden.size());
240     assertEquals("[1, 7]", Arrays.toString(hidden.get(0)));
241   }
242
243   /**
244    * Test the method that hides a specified column including any adjacent
245    * selected columns. This is a convenience method for the case where multiple
246    * column regions are selected and then hidden using menu option View | Hide |
247    * Selected Columns.
248    */
249   @Test(groups = { "Functional" })
250   public void testHideColumns_withSelection()
251   {
252     ColumnSelection cs = new ColumnSelection();
253     // select columns 4-6
254     cs.addElement(4);
255     cs.addElement(5);
256     cs.addElement(6);
257     // hide column 5 (and adjacent):
258     cs.hideColumns(5);
259     // 4,5,6 now hidden:
260     List<int[]> hidden = cs.getHiddenColumns();
261     assertEquals(1, hidden.size());
262     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
263     // none now selected:
264     assertTrue(cs.getSelected().isEmpty());
265
266     // repeat, hiding column 4 (5 and 6)
267     cs = new ColumnSelection();
268     cs.addElement(4);
269     cs.addElement(5);
270     cs.addElement(6);
271     cs.hideColumns(4);
272     hidden = cs.getHiddenColumns();
273     assertEquals(1, hidden.size());
274     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
275     assertTrue(cs.getSelected().isEmpty());
276
277     // repeat, hiding column (4, 5 and) 6
278     cs = new ColumnSelection();
279     cs.addElement(4);
280     cs.addElement(5);
281     cs.addElement(6);
282     cs.hideColumns(6);
283     hidden = cs.getHiddenColumns();
284     assertEquals(1, hidden.size());
285     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
286     assertTrue(cs.getSelected().isEmpty());
287
288     // repeat, with _only_ adjacent columns selected
289     cs = new ColumnSelection();
290     cs.addElement(4);
291     cs.addElement(6);
292     cs.hideColumns(5);
293     hidden = cs.getHiddenColumns();
294     assertEquals(1, hidden.size());
295     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
296     assertTrue(cs.getSelected().isEmpty());
297   }
298
299   /**
300    * Test the method that hides all (possibly disjoint) selected column ranges
301    */
302   @Test(groups = { "Functional" })
303   public void testHideSelectedColumns()
304   {
305     ColumnSelection cs = new ColumnSelection();
306     int[] sel = { 2, 3, 4, 7, 8, 9, 20, 21, 22 };
307     for (int col : sel)
308     {
309       cs.addElement(col);
310     }
311     cs.hideColumns(15, 18);
312
313     cs.hideSelectedColumns();
314     assertTrue(cs.getSelected().isEmpty());
315     List<int[]> hidden = cs.getHiddenColumns();
316     assertEquals(4, hidden.size());
317     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
318     assertEquals("[7, 9]", Arrays.toString(hidden.get(1)));
319     assertEquals("[15, 18]", Arrays.toString(hidden.get(2)));
320     assertEquals("[20, 22]", Arrays.toString(hidden.get(3)));
321   }
322
323   /**
324    * Test the method that gets runs of selected columns ordered by column. If
325    * this fails, HideSelectedColumns may also fail
326    */
327   @Test(groups = { "Functional" })
328   public void testGetSelectedRanges()
329   {
330     /*
331      * getSelectedRanges returns ordered columns regardless
332      * of the order in which they are added
333      */
334     ColumnSelection cs = new ColumnSelection();
335     int[] sel = { 4, 3, 7, 21, 9, 20, 8, 22, 2 };
336     for (int col : sel)
337     {
338       cs.addElement(col);
339     }
340     List<int[]> range;
341     range = cs.getSelectedRanges();
342     assertEquals(3, range.size());
343     assertEquals("[2, 4]", Arrays.toString(range.get(0)));
344     assertEquals("[7, 9]", Arrays.toString(range.get(1)));
345     assertEquals("[20, 22]", Arrays.toString(range.get(2)));
346     cs.addElement(0);
347     cs.addElement(1);
348     range = cs.getSelectedRanges();
349     assertEquals(3, range.size());
350     assertEquals("[0, 4]", Arrays.toString(range.get(0)));
351   }
352
353   /**
354    * Test the method that reveals a range of hidden columns given the start
355    * column of the range
356    */
357   @Test(groups = { "Functional" })
358   public void testRevealHiddenColumns()
359   {
360     ColumnSelection cs = new ColumnSelection();
361     cs.hideColumns(5, 8);
362     cs.addElement(10);
363     cs.revealHiddenColumns(5);
364     // hidden columns list now null but getter returns empty list:
365     assertTrue(cs.getHiddenColumns().isEmpty());
366     // revealed columns are marked as selected (added to selection):
367     assertEquals("[10, 5, 6, 7, 8]", cs.getSelected().toString());
368
369     // calling with a column other than the range start does nothing:
370     cs = new ColumnSelection();
371     cs.hideColumns(5, 8);
372     List<int[]> hidden = cs.getHiddenColumns();
373     cs.revealHiddenColumns(6);
374     assertSame(hidden, cs.getHiddenColumns());
375     assertTrue(cs.getSelected().isEmpty());
376   }
377
378   @Test(groups = { "Functional" })
379   public void testRevealAllHiddenColumns()
380   {
381     ColumnSelection cs = new ColumnSelection();
382     cs.hideColumns(5, 8);
383     cs.hideColumns(2, 3);
384     cs.addElement(11);
385     cs.addElement(1);
386     cs.revealAllHiddenColumns();
387
388     /*
389      * revealing hidden columns adds them (in order) to the (unordered)
390      * selection list
391      */
392     assertTrue(cs.getHiddenColumns().isEmpty());
393     assertEquals("[11, 1, 2, 3, 5, 6, 7, 8]", cs.getSelected().toString());
394   }
395
396   @Test(groups = { "Functional" })
397   public void testIsVisible()
398   {
399     ColumnSelection cs = new ColumnSelection();
400     cs.hideColumns(2, 4);
401     cs.hideColumns(6, 7);
402     assertTrue(cs.isVisible(0));
403     assertTrue(cs.isVisible(-99));
404     assertTrue(cs.isVisible(1));
405     assertFalse(cs.isVisible(2));
406     assertFalse(cs.isVisible(3));
407     assertFalse(cs.isVisible(4));
408     assertTrue(cs.isVisible(5));
409     assertFalse(cs.isVisible(6));
410     assertFalse(cs.isVisible(7));
411   }
412
413   @Test(groups = { "Functional" })
414   public void testGetVisibleContigs()
415   {
416     ColumnSelection cs = new ColumnSelection();
417     cs.hideColumns(3, 6);
418     cs.hideColumns(8, 9);
419     cs.hideColumns(12, 12);
420
421     // start position is inclusive, end position exclusive:
422     int[] visible = cs.getVisibleContigs(1, 13);
423     assertEquals("[1, 2, 7, 7, 10, 11]", Arrays.toString(visible));
424
425     visible = cs.getVisibleContigs(4, 14);
426     assertEquals("[7, 7, 10, 11, 13, 13]", Arrays.toString(visible));
427
428     visible = cs.getVisibleContigs(3, 10);
429     assertEquals("[7, 7]", Arrays.toString(visible));
430
431     visible = cs.getVisibleContigs(4, 6);
432     assertEquals("[]", Arrays.toString(visible));
433   }
434
435   @Test(groups = { "Functional" })
436   public void testInvertColumnSelection()
437   {
438     ColumnSelection cs = new ColumnSelection();
439     cs.addElement(4);
440     cs.addElement(6);
441     cs.addElement(8);
442     cs.hideColumns(3, 3);
443     cs.hideColumns(6, 6);
444
445     // invert selection from start (inclusive) to end (exclusive)
446     // hidden columns are _not_ changed
447     cs.invertColumnSelection(2, 9);
448     assertEquals("[2, 5, 7]", cs.getSelected().toString());
449
450     cs.invertColumnSelection(1, 9);
451     assertEquals("[1, 4, 8]", cs.getSelected().toString());
452   }
453
454   @Test(groups = { "Functional" })
455   public void testMaxColumnSelection()
456   {
457     ColumnSelection cs = new ColumnSelection();
458     cs.addElement(0);
459     cs.addElement(513);
460     cs.addElement(1);
461     assertEquals(513, cs.getMax());
462     cs.removeElement(513);
463     assertEquals(1, cs.getMax());
464     cs.removeElement(1);
465     assertEquals(0, cs.getMax());
466     cs.addElement(512);
467     cs.addElement(513);
468     assertEquals(513, cs.getMax());
469
470   }
471
472   @Test(groups = { "Functional" })
473   public void testMinColumnSelection()
474   {
475     ColumnSelection cs = new ColumnSelection();
476     cs.addElement(0);
477     cs.addElement(513);
478     cs.addElement(1);
479     assertEquals(0, cs.getMin());
480     cs.removeElement(0);
481     assertEquals(1, cs.getMin());
482     cs.addElement(0);
483     assertEquals(0, cs.getMin());
484   }
485
486   @Test(groups = { "Functional" })
487   public void testEquals()
488   {
489     ColumnSelection cs = new ColumnSelection();
490     cs.addElement(0);
491     cs.addElement(513);
492     cs.addElement(1);
493     cs.hideColumns(3);
494     cs.hideColumns(7);
495     cs.hideColumns(5,9);
496
497     // same selections added in a different order
498     ColumnSelection cs2 = new ColumnSelection();
499     cs2.addElement(1);
500     cs2.addElement(513);
501     cs2.addElement(0);
502
503     // with no hidden columns
504     assertFalse(cs.equals(cs2));
505     assertFalse(cs2.equals(cs));
506
507     // with hidden columns added in a different order
508     cs2.hideColumns(6, 9);
509     cs2.hideColumns(5, 8);
510     cs2.hideColumns(3);
511     
512     assertTrue(cs.equals(cs2));
513     assertTrue(cs.equals(cs));
514     assertTrue(cs2.equals(cs));
515     assertTrue(cs2.equals(cs2));
516
517     cs2.addElement(12);
518     assertFalse(cs.equals(cs2));
519     assertFalse(cs2.equals(cs));
520
521     cs2.removeElement(12);
522     assertTrue(cs.equals(cs2));
523
524     cs2.hideColumns(88);
525     assertFalse(cs.equals(cs2));
526     /*
527      * unhiding a column adds it to selection!
528      */
529     cs2.revealHiddenColumns(88);
530     assertFalse(cs.equals(cs2));
531     cs.addElement(88);
532     assertTrue(cs.equals(cs2));
533   }
534
535   /**
536    * Test the method that returns selected columns, in the order in which they
537    * were added
538    */
539   @Test(groups = { "Functional" })
540   public void testGetSelection()
541   {
542     ColumnSelection cs = new ColumnSelection();
543     int[] sel = { 4, 3, 7, 21 };
544     for (int col : sel)
545     {
546       cs.addElement(col);
547     }
548     List<Integer> selected = cs.getSelected();
549     assertEquals(4, selected.size());
550     int i = 0;
551     for (int col : sel)
552     {
553       assertEquals(col, selected.get(i++).intValue());
554     }
555
556     cs.removeElement(7);
557     cs.addElement(1);
558     cs.removeElement(4);
559     selected = cs.getSelected();
560     assertEquals(3, selected.size());
561     assertEquals(3, selected.get(0).intValue());
562     assertEquals(21, selected.get(1).intValue());
563     assertEquals(1, selected.get(2).intValue());
564   }
565 }