JAL-2388 New hidden cols/seqs functions to support overview panel
[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 import static org.testng.AssertJUnit.fail;
28
29 import jalview.gui.JvOptionPane;
30
31 import java.util.Arrays;
32 import java.util.BitSet;
33 import java.util.Collections;
34 import java.util.ConcurrentModificationException;
35 import java.util.List;
36
37 import org.testng.annotations.BeforeClass;
38 import org.testng.annotations.Test;
39
40 public class ColumnSelectionTest
41 {
42
43   @BeforeClass(alwaysRun = true)
44   public void setUpJvOptionPane()
45   {
46     JvOptionPane.setInteractiveMode(false);
47     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
48   }
49
50   @Test(groups = { "Functional" })
51   public void testAddElement()
52   {
53     ColumnSelection cs = new ColumnSelection();
54     cs.addElement(2);
55     cs.addElement(5);
56     cs.addElement(3);
57     cs.addElement(5); // ignored
58     List<Integer> sel = cs.getSelected();
59     assertEquals("[2, 5, 3]", sel.toString());
60   }
61
62   /**
63    * Test the remove method - in particular to verify that remove(int i) removes
64    * the element whose value is i, _NOT_ the i'th element.
65    */
66   @Test(groups = { "Functional" })
67   public void testRemoveElement()
68   {
69     ColumnSelection cs = new ColumnSelection();
70     cs.addElement(2);
71     cs.addElement(5);
72
73     // removing elements not in the list has no effect
74     cs.removeElement(0);
75     cs.removeElement(1);
76     List<Integer> sel = cs.getSelected();
77     assertEquals(2, sel.size());
78     assertEquals(new Integer(2), sel.get(0));
79     assertEquals(new Integer(5), sel.get(1));
80
81     // removing an element in the list removes it
82     cs.removeElement(2);
83     // ...and also from the read-only view
84     assertEquals(1, sel.size());
85     sel = cs.getSelected();
86     assertEquals(1, sel.size());
87     assertEquals(new Integer(5), sel.get(0));
88   }
89
90   /**
91    * Test the method that finds the visible column position of an alignment
92    * column, allowing for hidden columns.
93    */
94   @Test(groups = { "Functional" })
95   public void testFindColumnPosition()
96   {
97     ColumnSelection cs = new ColumnSelection();
98     assertEquals(5, cs.findColumnPosition(5));
99
100     // hiding column 6 makes no difference
101     cs.hideColumns(6, 6);
102     assertEquals(5, cs.findColumnPosition(5));
103
104     // hiding column 4 moves column 5 to column 4
105     cs.hideColumns(4, 4);
106     assertEquals(4, cs.findColumnPosition(5));
107
108     // hiding column 4 moves column 4 to position 3
109     assertEquals(3, cs.findColumnPosition(4));
110
111     // hiding columns 1 and 2 moves column 5 to column 2
112     cs.hideColumns(1, 2);
113     assertEquals(2, cs.findColumnPosition(5));
114
115     // check with > 1 hidden column regions
116     // where some columns are in the hidden regions
117     ColumnSelection cs2 = new ColumnSelection();
118     cs2.hideColumns(5, 10);
119     cs2.hideColumns(20, 27);
120     cs2.hideColumns(40, 44);
121
122     // hiding columns 5-10 and 20-27 moves column 8 to column 4
123     assertEquals(4, cs2.findColumnPosition(8));
124
125     // and moves column 24 to 13
126     assertEquals(13, cs2.findColumnPosition(24));
127
128     // and moves column 28 to 14
129     assertEquals(14, cs2.findColumnPosition(28));
130
131     // and moves column 40 to 25
132     assertEquals(25, cs2.findColumnPosition(40));
133
134     // check when hidden columns start at 0 that the visible column
135     // is returned as 0
136     ColumnSelection cs3 = new ColumnSelection();
137     cs3.hideColumns(0, 4);
138     assertEquals(0, cs3.findColumnPosition(2));
139
140   }
141
142   /**
143    * Test the method that finds the visible column position a given distance
144    * before another column
145    */
146   @Test(groups = { "Functional" })
147   public void testFindColumnNToLeft()
148   {
149     ColumnSelection cs = new ColumnSelection();
150
151     // test that without hidden columns, findColumnNToLeft returns
152     // position n to left of provided position
153     int pos = cs.findColumnNToLeft(3, 10);
154     assertEquals(7, pos);
155
156     // 0 returns same position
157     pos = cs.findColumnNToLeft(0, 10);
158     assertEquals(10, pos);
159
160     // overflow to left returns negative number
161     pos = cs.findColumnNToLeft(3, 0);
162     assertEquals(-3, pos);
163
164     // test that with hidden columns to left of result column
165     // behaviour is the same as above
166     cs.hideColumns(1, 3);
167
168     // position n to left of provided position
169     pos = cs.findColumnNToLeft(3, 10);
170     assertEquals(7, pos);
171
172     // 0 returns same position
173     pos = cs.findColumnNToLeft(0, 10);
174     assertEquals(10, pos);
175
176     // test with one set of hidden columns between start and required position
177     cs.hideColumns(12, 15);
178     pos = cs.findColumnNToLeft(8, 17);
179     assertEquals(5, pos);
180
181     // test with two sets of hidden columns between start and required position
182     cs.hideColumns(20, 21);
183     pos = cs.findColumnNToLeft(8, 23);
184     assertEquals(9, pos);
185
186     // repeat last 2 tests with no hidden columns to left of required position
187     cs.revealAllHiddenColumns();
188
189     // test with one set of hidden columns between start and required position
190     cs.hideColumns(12, 15);
191     pos = cs.findColumnNToLeft(8, 17);
192     assertEquals(5, pos);
193
194     // test with two sets of hidden columns between start and required position
195     cs.hideColumns(20, 21);
196     pos = cs.findColumnNToLeft(8, 23);
197     assertEquals(9, pos);
198
199   }
200
201   /**
202    * Test the code used to locate the reference sequence ruler origin
203    */
204   @Test(groups = { "Functional" })
205   public void testLocateVisibleBoundsofSequence()
206   {
207     ColumnSelection cs = new ColumnSelection();
208     SequenceI seq = new Sequence("RefSeq", "-A-SD-ASD--E---");
209     assertEquals(2, seq.findIndex(seq.getStart()));
210
211     // no hidden columns
212     assertEquals(
213             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
214                 seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
215                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
216                 seq.findIndex(seq.getEnd()) - 1 }),
217             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
218
219     // hidden column on gap after end of sequence - should not affect bounds
220     cs.hideColumns(13);
221     assertEquals(
222             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
223                 seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
224                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
225                 seq.findIndex(seq.getEnd()) - 1 }),
226             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
227
228     cs.revealAllHiddenColumns();
229     // hidden column on gap before beginning of sequence - should vis bounds by
230     // one
231     cs.hideColumns(0);
232     assertEquals(
233             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 2,
234                 seq.findIndex(seq.getEnd()) - 2, seq.getStart(),
235                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
236                 seq.findIndex(seq.getEnd()) - 1 }),
237             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
238
239     cs.revealAllHiddenColumns();
240     // hide columns around most of sequence - leave one residue remaining
241     cs.hideColumns(1, 3);
242     cs.hideColumns(6, 11);
243     assertEquals("-D",
244             cs.getVisibleSequenceStrings(0, 5, new SequenceI[] { seq })[0]);
245     assertEquals(
246             Arrays.toString(new int[] { 1, 1, 3, 3,
247                 seq.findIndex(seq.getStart()) - 1,
248                 seq.findIndex(seq.getEnd()) - 1 }),
249             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
250     cs.revealAllHiddenColumns();
251
252     // hide whole sequence - should just get location of hidden region
253     // containing sequence
254     cs.hideColumns(1, 11);
255     assertEquals(
256             Arrays.toString(new int[] { 0, 1, 0, 0,
257                 seq.findIndex(seq.getStart()) - 1,
258                 seq.findIndex(seq.getEnd()) - 1 }),
259             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
260
261   }
262
263   @Test(groups = { "Functional" })
264   public void testLocateVisibleBoundsPathologicals()
265   {
266     // test some pathological cases we missed
267     AlignmentI al = new Alignment(new SequenceI[] { new Sequence(
268             "refseqGaptest", "KTDVTI----------NFI-----G----L") });
269     ColumnSelection cs = new ColumnSelection();
270     cs.hideInsertionsFor(al.getSequenceAt(0));
271     assertEquals(
272             "G",
273             ""
274                     + al.getSequenceAt(0).getCharAt(
275                             cs.adjustForHiddenColumns(9)));
276
277   }
278
279   @Test(groups = { "Functional" })
280   public void testHideColumns()
281   {
282     ColumnSelection cs = new ColumnSelection();
283     cs.hideColumns(5);
284     List<int[]> hidden = cs.getHiddenColumns();
285     assertEquals(1, hidden.size());
286     assertEquals("[5, 5]", Arrays.toString(hidden.get(0)));
287
288     cs.hideColumns(3);
289     assertEquals(2, hidden.size());
290     // two hidden ranges, in order:
291     assertSame(hidden, cs.getHiddenColumns());
292     assertEquals("[3, 3]", Arrays.toString(hidden.get(0)));
293     assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
294
295     // hiding column 4 expands [3, 3] to [3, 4]
296     // and merges to [5, 5] to make [3, 5]
297     cs.hideColumns(4);
298     hidden = cs.getHiddenColumns();
299     assertEquals(1, hidden.size());
300     assertEquals("[3, 5]", Arrays.toString(hidden.get(0)));
301
302     // clear hidden columns (note they are added to selected)
303     cs.revealAllHiddenColumns();
304     // it is now actually null but getter returns an empty list
305     assertTrue(cs.getHiddenColumns().isEmpty());
306
307     cs.hideColumns(3, 6);
308     hidden = cs.getHiddenColumns();
309     int[] firstHiddenRange = hidden.get(0);
310     assertEquals("[3, 6]", Arrays.toString(firstHiddenRange));
311
312     // adding a subrange of already hidden should do nothing
313     cs.hideColumns(4, 5);
314     assertEquals(1, hidden.size());
315     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
316     cs.hideColumns(3, 5);
317     assertEquals(1, hidden.size());
318     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
319     cs.hideColumns(4, 6);
320     assertEquals(1, hidden.size());
321     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
322     cs.hideColumns(3, 6);
323     assertEquals(1, hidden.size());
324     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
325
326     cs.revealAllHiddenColumns();
327     cs.hideColumns(2, 4);
328     hidden = cs.getHiddenColumns();
329     assertEquals(1, hidden.size());
330     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
331
332     // extend contiguous with 2 positions overlap
333     cs.hideColumns(3, 5);
334     assertEquals(1, hidden.size());
335     assertEquals("[2, 5]", Arrays.toString(hidden.get(0)));
336
337     // extend contiguous with 1 position overlap
338     cs.hideColumns(5, 6);
339     assertEquals(1, hidden.size());
340     assertEquals("[2, 6]", Arrays.toString(hidden.get(0)));
341
342     // extend contiguous with overlap both ends:
343     cs.hideColumns(1, 7);
344     assertEquals(1, hidden.size());
345     assertEquals("[1, 7]", Arrays.toString(hidden.get(0)));
346   }
347
348   /**
349    * Test the method that hides a specified column including any adjacent
350    * selected columns. This is a convenience method for the case where multiple
351    * column regions are selected and then hidden using menu option View | Hide |
352    * Selected Columns.
353    */
354   @Test(groups = { "Functional" })
355   public void testHideColumns_withSelection()
356   {
357     ColumnSelection cs = new ColumnSelection();
358     // select columns 4-6
359     cs.addElement(4);
360     cs.addElement(5);
361     cs.addElement(6);
362     // hide column 5 (and adjacent):
363     cs.hideColumns(5);
364     // 4,5,6 now hidden:
365     List<int[]> hidden = cs.getHiddenColumns();
366     assertEquals(1, hidden.size());
367     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
368     // none now selected:
369     assertTrue(cs.getSelected().isEmpty());
370
371     // repeat, hiding column 4 (5 and 6)
372     cs = new ColumnSelection();
373     cs.addElement(4);
374     cs.addElement(5);
375     cs.addElement(6);
376     cs.hideColumns(4);
377     hidden = cs.getHiddenColumns();
378     assertEquals(1, hidden.size());
379     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
380     assertTrue(cs.getSelected().isEmpty());
381
382     // repeat, hiding column (4, 5 and) 6
383     cs = new ColumnSelection();
384     cs.addElement(4);
385     cs.addElement(5);
386     cs.addElement(6);
387     cs.hideColumns(6);
388     hidden = cs.getHiddenColumns();
389     assertEquals(1, hidden.size());
390     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
391     assertTrue(cs.getSelected().isEmpty());
392
393     // repeat, with _only_ adjacent columns selected
394     cs = new ColumnSelection();
395     cs.addElement(4);
396     cs.addElement(6);
397     cs.hideColumns(5);
398     hidden = cs.getHiddenColumns();
399     assertEquals(1, hidden.size());
400     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
401     assertTrue(cs.getSelected().isEmpty());
402   }
403
404   /**
405    * Test the method that hides all (possibly disjoint) selected column ranges
406    */
407   @Test(groups = { "Functional" })
408   public void testHideSelectedColumns()
409   {
410     ColumnSelection cs = new ColumnSelection();
411     int[] sel = { 2, 3, 4, 7, 8, 9, 20, 21, 22 };
412     for (int col : sel)
413     {
414       cs.addElement(col);
415     }
416     cs.hideColumns(15, 18);
417
418     cs.hideSelectedColumns();
419     assertTrue(cs.getSelected().isEmpty());
420     List<int[]> hidden = cs.getHiddenColumns();
421     assertEquals(4, hidden.size());
422     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
423     assertEquals("[7, 9]", Arrays.toString(hidden.get(1)));
424     assertEquals("[15, 18]", Arrays.toString(hidden.get(2)));
425     assertEquals("[20, 22]", Arrays.toString(hidden.get(3)));
426   }
427
428   /**
429    * Test the method that gets runs of selected columns ordered by column. If
430    * this fails, HideSelectedColumns may also fail
431    */
432   @Test(groups = { "Functional" })
433   public void testGetSelectedRanges()
434   {
435     /*
436      * getSelectedRanges returns ordered columns regardless
437      * of the order in which they are added
438      */
439     ColumnSelection cs = new ColumnSelection();
440     int[] sel = { 4, 3, 7, 21, 9, 20, 8, 22, 2 };
441     for (int col : sel)
442     {
443       cs.addElement(col);
444     }
445     List<int[]> range;
446     range = cs.getSelectedRanges();
447     assertEquals(3, range.size());
448     assertEquals("[2, 4]", Arrays.toString(range.get(0)));
449     assertEquals("[7, 9]", Arrays.toString(range.get(1)));
450     assertEquals("[20, 22]", Arrays.toString(range.get(2)));
451     cs.addElement(0);
452     cs.addElement(1);
453     range = cs.getSelectedRanges();
454     assertEquals(3, range.size());
455     assertEquals("[0, 4]", Arrays.toString(range.get(0)));
456   }
457
458   /**
459    * Test the method that reveals a range of hidden columns given the start
460    * column of the range
461    */
462   @Test(groups = { "Functional" })
463   public void testRevealHiddenColumns()
464   {
465     ColumnSelection cs = new ColumnSelection();
466     cs.hideColumns(5, 8);
467     cs.addElement(10);
468     cs.revealHiddenColumns(5);
469     // hidden columns list now null but getter returns empty list:
470     assertTrue(cs.getHiddenColumns().isEmpty());
471     // revealed columns are marked as selected (added to selection):
472     assertEquals("[10, 5, 6, 7, 8]", cs.getSelected().toString());
473
474     // calling with a column other than the range start does nothing:
475     cs = new ColumnSelection();
476     cs.hideColumns(5, 8);
477     List<int[]> hidden = cs.getHiddenColumns();
478     cs.revealHiddenColumns(6);
479     assertSame(hidden, cs.getHiddenColumns());
480     assertTrue(cs.getSelected().isEmpty());
481   }
482
483   @Test(groups = { "Functional" })
484   public void testRevealAllHiddenColumns()
485   {
486     ColumnSelection cs = new ColumnSelection();
487     cs.hideColumns(5, 8);
488     cs.hideColumns(2, 3);
489     cs.addElement(11);
490     cs.addElement(1);
491     cs.revealAllHiddenColumns();
492
493     /*
494      * revealing hidden columns adds them (in order) to the (unordered)
495      * selection list
496      */
497     assertTrue(cs.getHiddenColumns().isEmpty());
498     assertEquals("[11, 1, 2, 3, 5, 6, 7, 8]", cs.getSelected().toString());
499   }
500
501   @Test(groups = { "Functional" })
502   public void testIsVisible()
503   {
504     ColumnSelection cs = new ColumnSelection();
505     cs.hideColumns(2, 4);
506     cs.hideColumns(6, 7);
507     assertTrue(cs.isVisible(0));
508     assertTrue(cs.isVisible(-99));
509     assertTrue(cs.isVisible(1));
510     assertFalse(cs.isVisible(2));
511     assertFalse(cs.isVisible(3));
512     assertFalse(cs.isVisible(4));
513     assertTrue(cs.isVisible(5));
514     assertFalse(cs.isVisible(6));
515     assertFalse(cs.isVisible(7));
516   }
517
518   @Test(groups = { "Functional" })
519   public void testGetVisibleContigs()
520   {
521     ColumnSelection cs = new ColumnSelection();
522     cs.hideColumns(3, 6);
523     cs.hideColumns(8, 9);
524     cs.hideColumns(12, 12);
525
526     // start position is inclusive, end position exclusive:
527     int[] visible = cs.getVisibleContigs(1, 13);
528     assertEquals("[1, 2, 7, 7, 10, 11]", Arrays.toString(visible));
529
530     visible = cs.getVisibleContigs(4, 14);
531     assertEquals("[7, 7, 10, 11, 13, 13]", Arrays.toString(visible));
532
533     visible = cs.getVisibleContigs(3, 10);
534     assertEquals("[7, 7]", Arrays.toString(visible));
535
536     visible = cs.getVisibleContigs(4, 6);
537     assertEquals("[]", Arrays.toString(visible));
538   }
539
540   @Test(groups = { "Functional" })
541   public void testInvertColumnSelection()
542   {
543     ColumnSelection cs = new ColumnSelection();
544     cs.addElement(4);
545     cs.addElement(6);
546     cs.addElement(8);
547     cs.hideColumns(3, 3);
548     cs.hideColumns(6, 6);
549
550     // invert selection from start (inclusive) to end (exclusive)
551     // hidden columns are _not_ changed
552     cs.invertColumnSelection(2, 9);
553     assertEquals("[2, 5, 7]", cs.getSelected().toString());
554
555     cs.invertColumnSelection(1, 9);
556     assertEquals("[1, 4, 8]", cs.getSelected().toString());
557   }
558
559   @Test(groups = { "Functional" })
560   public void testMaxColumnSelection()
561   {
562     ColumnSelection cs = new ColumnSelection();
563     cs.addElement(0);
564     cs.addElement(513);
565     cs.addElement(1);
566     assertEquals(513, cs.getMax());
567     cs.removeElement(513);
568     assertEquals(1, cs.getMax());
569     cs.removeElement(1);
570     assertEquals(0, cs.getMax());
571     cs.addElement(512);
572     cs.addElement(513);
573     assertEquals(513, cs.getMax());
574
575   }
576
577   @Test(groups = { "Functional" })
578   public void testMinColumnSelection()
579   {
580     ColumnSelection cs = new ColumnSelection();
581     cs.addElement(0);
582     cs.addElement(513);
583     cs.addElement(1);
584     assertEquals(0, cs.getMin());
585     cs.removeElement(0);
586     assertEquals(1, cs.getMin());
587     cs.addElement(0);
588     assertEquals(0, cs.getMin());
589   }
590
591   @Test(groups = { "Functional" })
592   public void testEquals()
593   {
594     ColumnSelection cs = new ColumnSelection();
595     cs.addElement(0);
596     cs.addElement(513);
597     cs.addElement(1);
598     cs.hideColumns(3);
599     cs.hideColumns(7);
600     cs.hideColumns(5, 9);
601
602     // same selections added in a different order
603     ColumnSelection cs2 = new ColumnSelection();
604     cs2.addElement(1);
605     cs2.addElement(513);
606     cs2.addElement(0);
607
608     // with no hidden columns
609     assertFalse(cs.equals(cs2));
610     assertFalse(cs2.equals(cs));
611
612     // with hidden columns added in a different order
613     cs2.hideColumns(6, 9);
614     cs2.hideColumns(5, 8);
615     cs2.hideColumns(3);
616
617     assertTrue(cs.equals(cs2));
618     assertTrue(cs.equals(cs));
619     assertTrue(cs2.equals(cs));
620     assertTrue(cs2.equals(cs2));
621
622     cs2.addElement(12);
623     assertFalse(cs.equals(cs2));
624     assertFalse(cs2.equals(cs));
625
626     cs2.removeElement(12);
627     assertTrue(cs.equals(cs2));
628
629     cs2.hideColumns(88);
630     assertFalse(cs.equals(cs2));
631     /*
632      * unhiding a column adds it to selection!
633      */
634     cs2.revealHiddenColumns(88);
635     assertFalse(cs.equals(cs2));
636     cs.addElement(88);
637     assertTrue(cs.equals(cs2));
638   }
639
640   /**
641    * Test the method that returns selected columns, in the order in which they
642    * were added
643    */
644   @Test(groups = { "Functional" })
645   public void testGetSelected()
646   {
647     ColumnSelection cs = new ColumnSelection();
648     int[] sel = { 4, 3, 7, 21 };
649     for (int col : sel)
650     {
651       cs.addElement(col);
652     }
653
654     List<Integer> selected = cs.getSelected();
655     assertEquals(4, selected.size());
656     assertEquals("[4, 3, 7, 21]", selected.toString());
657
658     /*
659      * getSelected returns a read-only view of the list
660      * verify the view follows any changes in it
661      */
662     cs.removeElement(7);
663     cs.addElement(1);
664     cs.removeElement(4);
665     assertEquals("[3, 21, 1]", selected.toString());
666   }
667
668   /**
669    * Test to verify that the list returned by getSelection cannot be modified
670    */
671   @Test(groups = { "Functional" })
672   public void testGetSelected_isReadOnly()
673   {
674     ColumnSelection cs = new ColumnSelection();
675     cs.addElement(3);
676
677     List<Integer> selected = cs.getSelected();
678     try
679     {
680       selected.clear();
681       fail("expected exception");
682     } catch (UnsupportedOperationException e)
683     {
684       // expected
685     }
686     try
687     {
688       selected.add(1);
689       fail("expected exception");
690     } catch (UnsupportedOperationException e)
691     {
692       // expected
693     }
694     try
695     {
696       selected.remove(3);
697       fail("expected exception");
698     } catch (UnsupportedOperationException e)
699     {
700       // expected
701     }
702     try
703     {
704       Collections.sort(selected);
705       fail("expected exception");
706     } catch (UnsupportedOperationException e)
707     {
708       // expected
709     }
710   }
711
712   /**
713    * Test that demonstrates a ConcurrentModificationException is thrown if you
714    * change the selection while iterating over it
715    */
716   @Test(
717     groups = "Functional",
718     expectedExceptions = { ConcurrentModificationException.class })
719   public void testGetSelected_concurrentModification()
720   {
721     ColumnSelection cs = new ColumnSelection();
722     cs.addElement(0);
723     cs.addElement(1);
724     cs.addElement(2);
725
726     /*
727      * simulate changing the list under us (e.g. in a separate
728      * thread) while iterating over it -> ConcurrentModificationException
729      */
730     List<Integer> selected = cs.getSelected();
731     for (Integer col : selected)
732     {
733       if (col.intValue() == 0)
734       {
735         cs.removeElement(1);
736       }
737     }
738   }
739
740   @Test(groups = "Functional")
741   public void testMarkColumns()
742   {
743     ColumnSelection cs = new ColumnSelection();
744     cs.addElement(5); // this will be cleared
745     BitSet toMark = new BitSet();
746     toMark.set(1);
747     toMark.set(3);
748     toMark.set(6);
749     toMark.set(9);
750
751     assertTrue(cs.markColumns(toMark, 3, 8, false, false, false));
752     List<Integer> selected = cs.getSelected();
753     assertEquals(2, selected.size());
754     assertTrue(selected.contains(3));
755     assertTrue(selected.contains(6));
756   }
757
758   @Test(groups = "Functional")
759   public void testMarkColumns_extend()
760   {
761     ColumnSelection cs = new ColumnSelection();
762     cs.addElement(1);
763     cs.addElement(5);
764     BitSet toMark = new BitSet();
765     toMark.set(1);
766     toMark.set(3);
767     toMark.set(6);
768     toMark.set(9);
769
770     /*
771      * extending selection of {3, 6} should leave {1, 3, 5, 6} selected
772      */
773     assertTrue(cs.markColumns(toMark, 3, 8, false, true, false));
774     List<Integer> selected = cs.getSelected();
775     assertEquals(4, selected.size());
776     assertTrue(selected.contains(1));
777     assertTrue(selected.contains(3));
778     assertTrue(selected.contains(5));
779     assertTrue(selected.contains(6));
780   }
781
782   @Test(groups = "Functional")
783   public void testMarkColumns_invert()
784   {
785     ColumnSelection cs = new ColumnSelection();
786     cs.addElement(5); // this will be cleared
787     BitSet toMark = new BitSet();
788     toMark.set(1);
789     toMark.set(3);
790     toMark.set(6);
791     toMark.set(9);
792
793     /*
794      * inverted selection of {3, 6} should select {4, 5, 7, 8}
795      */
796     assertTrue(cs.markColumns(toMark, 3, 8, true, false, false));
797     List<Integer> selected = cs.getSelected();
798     assertEquals(4, selected.size());
799     assertTrue(selected.contains(4));
800     assertTrue(selected.contains(5));
801     assertTrue(selected.contains(7));
802     assertTrue(selected.contains(8));
803   }
804
805   @Test(groups = "Functional")
806   public void testMarkColumns_toggle()
807   {
808     ColumnSelection cs = new ColumnSelection();
809     cs.addElement(1); // outside change range
810     cs.addElement(3);
811     cs.addElement(4);
812     cs.addElement(10); // outside change range
813     BitSet toMark = new BitSet();
814     toMark.set(1);
815     toMark.set(3);
816     toMark.set(6);
817     toMark.set(9);
818
819     /*
820      * toggling state of {3, 6} should leave {1, 4, 6, 10} selected
821      */
822     assertTrue(cs.markColumns(toMark, 3, 8, false, false, true));
823     List<Integer> selected = cs.getSelected();
824     assertEquals(4, selected.size());
825     assertTrue(selected.contains(1));
826     assertTrue(selected.contains(4));
827     assertTrue(selected.contains(6));
828     assertTrue(selected.contains(10));
829   }
830
831   @Test(groups = "Functional")
832   public void testCopyConstructor()
833   {
834     ColumnSelection cs = new ColumnSelection();
835     cs.addElement(3);
836     cs.addElement(1);
837     cs.hideColumns(10, 11);
838     cs.hideColumns(5, 7);
839     assertEquals("[5, 7]", Arrays.toString(cs.getHiddenColumns().get(0)));
840
841     ColumnSelection cs2 = new ColumnSelection(cs);
842     assertTrue(cs2.hasSelectedColumns());
843     assertTrue(cs2.hasHiddenColumns());
844     // order of column selection is preserved
845     assertEquals("[3, 1]", cs2.getSelected().toString());
846     assertEquals(2, cs2.getHiddenColumns().size());
847     // hidden columns are held in column order
848     assertEquals("[5, 7]", Arrays.toString(cs2.getHiddenColumns().get(0)));
849     assertEquals("[10, 11]", Arrays.toString(cs2.getHiddenColumns().get(1)));
850   }
851
852   /**
853    * Test for the case when a hidden range encloses more one already hidden
854    * range
855    */
856   @Test(groups = { "Functional" })
857   public void testHideColumns_subsumingHidden()
858   {
859     /*
860      * JAL-2370 bug scenario:
861      * two hidden ranges subsumed by a third
862      */
863     ColumnSelection cs = new ColumnSelection();
864     cs.hideColumns(49, 59);
865     cs.hideColumns(69, 79);
866     List<int[]> hidden = cs.getHiddenColumns();
867     assertEquals(2, hidden.size());
868     assertEquals("[49, 59]", Arrays.toString(hidden.get(0)));
869     assertEquals("[69, 79]", Arrays.toString(hidden.get(1)));
870   
871     cs.hideColumns(48, 80);
872     hidden = cs.getHiddenColumns();
873     assertEquals(1, hidden.size());
874     assertEquals("[48, 80]", Arrays.toString(hidden.get(0)));
875
876     /*
877      * another...joining hidden ranges
878      */
879     cs = new ColumnSelection();
880     cs.hideColumns(10, 20);
881     cs.hideColumns(30, 40);
882     cs.hideColumns(50, 60);
883     // hiding 21-49 should merge to one range
884     cs.hideColumns(21, 49);
885     hidden = cs.getHiddenColumns();
886     assertEquals(1, hidden.size());
887     assertEquals("[10, 60]", Arrays.toString(hidden.get(0)));
888
889     /*
890      * another...lef overlap, subsumption, right overlap,
891      * no overlap of existing hidden ranges
892      */
893     cs = new ColumnSelection();
894     cs.hideColumns(10, 20);
895     cs.hideColumns(10, 20);
896     cs.hideColumns(30, 35);
897     cs.hideColumns(40, 50);
898     cs.hideColumns(60, 70);
899
900     cs.hideColumns(15, 45);
901     hidden = cs.getHiddenColumns();
902     assertEquals(2, hidden.size());
903     assertEquals("[10, 50]", Arrays.toString(hidden.get(0)));
904     assertEquals("[60, 70]", Arrays.toString(hidden.get(1)));
905   }
906
907   @Test(groups = { "Functional" })
908   public void testStretchGroup_expand()
909   {
910     /*
911      * test that emulates clicking column 4 (selected)
912      * and dragging right to column 5 (all base 0)
913      */
914     ColumnSelection cs = new ColumnSelection();
915     cs.addElement(4);
916     SequenceGroup sg = new SequenceGroup();
917     sg.setStartRes(4);
918     sg.setEndRes(4);
919     cs.stretchGroup(5, sg, 4, 4);
920     assertEquals(cs.getSelected().size(), 2);
921     assertTrue(cs.contains(4));
922     assertTrue(cs.contains(5));
923     assertEquals(sg.getStartRes(), 4);
924     assertEquals(sg.getEndRes(), 5);
925
926     /*
927      * emulate drag right with columns 10-20 already selected
928      */
929     cs.clear();
930     for (int i = 10; i <= 20; i++)
931     {
932       cs.addElement(i);
933     }
934     assertEquals(cs.getSelected().size(), 11);
935     sg = new SequenceGroup();
936     sg.setStartRes(10);
937     sg.setEndRes(20);
938     cs.stretchGroup(21, sg, 10, 20);
939     assertEquals(cs.getSelected().size(), 12);
940     assertTrue(cs.contains(10));
941     assertTrue(cs.contains(21));
942     assertEquals(sg.getStartRes(), 10);
943     assertEquals(sg.getEndRes(), 21);
944   }
945
946   @Test(groups = { "Functional" })
947   public void testStretchGroup_shrink()
948   {
949     /*
950      * emulate drag left to 19 with columns 10-20 already selected
951      */
952     ColumnSelection cs = new ColumnSelection();
953     for (int i = 10; i <= 20; i++)
954     {
955       cs.addElement(i);
956     }
957     assertEquals(cs.getSelected().size(), 11);
958     SequenceGroup sg = new SequenceGroup();
959     sg.setStartRes(10);
960     sg.setEndRes(20);
961     cs.stretchGroup(19, sg, 10, 20);
962     assertEquals(cs.getSelected().size(), 10);
963     assertTrue(cs.contains(10));
964     assertTrue(cs.contains(19));
965     assertFalse(cs.contains(20));
966     assertEquals(sg.getStartRes(), 10);
967     assertEquals(sg.getEndRes(), 19);
968   }
969 }