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